react-native-doctor-ci 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/version.ts","../src/cache.ts","../src/concurrency.ts","../src/http.ts","../src/sources/directory.ts","../src/sources/npm.ts","../src/sources/github.ts","../src/enrich.ts","../src/policy.ts","../src/policy-file.ts","../src/report.ts","../src/report-json.ts","../src/report-sarif.ts","../src/report-pretty.ts","../src/report-annotations.ts","../src/package-json.ts","../src/changed-deps.ts","../src/git.ts","../src/workspaces.ts"],"sourcesContent":["/**\n * Tool version, kept in a leaf module so reporters can embed it without\n * importing the package entry point (which would create an import cycle).\n * @packageDocumentation\n */\n\n/**\n * The rn-doctor tool version. Kept in sync with package.json by release-it.\n */\nexport const VERSION = \"0.0.0\";\n","/**\n * Disk cache for enriched dependencies with 24h TTL and degraded-entry semantics.\n * @packageDocumentation\n */\n\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport type { EnrichedDependency, Signal, UnknownReason } from \"./types.js\";\n\nexport interface CacheEntry {\n readonly fetchedAt: string; // ISO 8601\n readonly enriched: EnrichedDependency;\n}\n\nexport interface CacheFile {\n readonly version: 1;\n readonly entries: Record<string, CacheEntry>;\n}\n\nconst CACHE_VERSION = 1;\nconst TTL_MS = 24 * 60 * 60 * 1000; // 24 hours\n\n/**\n * Determine if a cache entry is \"degraded\" (has transient warnings).\n * Transient reasons indicate the entry may be incomplete and shouldn't be re-used across runs.\n */\nfunction isDegraded(entry: CacheEntry): boolean {\n // Only *transient* unknowns (a blip, a rate-limit) make an entry degraded. Durable\n // unknowns (no repo URL, not in the directory, a real 404) are the true answer and\n // are safe to cache — re-fetching wouldn't change them.\n const transientReasons = new Set<UnknownReason>([\n \"github-rate-limited\",\n \"github-error\",\n \"npm-lookup-failed\",\n ]);\n\n const dep = entry.enriched;\n const signals: Signal<unknown>[] = [\n dep.npm.deprecated,\n dep.npm.hasCodegenConfig,\n dep.npm.hasReactNativePeerDep,\n dep.npm.hasNativeDirsHint,\n dep.github.archived,\n dep.github.pushedAt,\n dep.lastPublish,\n ];\n\n return signals.some((s) => !s.known && transientReasons.has(s.reason));\n}\n\n/**\n * Read the cache file from disk. Returns an empty cache if missing or corrupt.\n */\nexport async function readCacheFile(cacheDir: string): Promise<CacheFile> {\n const cacheFile = path.join(cacheDir, \".rn-doctor-cache.json\");\n\n try {\n const content = await fs.promises.readFile(cacheFile, \"utf-8\");\n const parsed = JSON.parse(content) as CacheFile;\n\n if (parsed.version !== CACHE_VERSION) {\n return { version: CACHE_VERSION, entries: {} };\n }\n\n return parsed;\n } catch {\n // Missing file or parse error — return empty cache, never throw\n return { version: CACHE_VERSION, entries: {} };\n }\n}\n\n/**\n * Write the cache file to disk.\n */\nexport async function writeCacheFile(cacheDir: string, cache: CacheFile): Promise<void> {\n const cacheFile = path.join(cacheDir, \".rn-doctor-cache.json\");\n\n await fs.promises.writeFile(cacheFile, JSON.stringify(cache, null, 2), \"utf-8\");\n}\n\n/**\n * Get a fresh entry from cache if available (not expired, not degraded).\n * Returns undefined if not cached, expired, or degraded (for next-run persistence).\n */\nexport function getFreshEntry(\n cache: CacheFile,\n packageName: string,\n now: Date = new Date(),\n): CacheEntry | undefined {\n const entry = cache.entries[packageName];\n\n if (!entry) {\n return undefined;\n }\n\n const age = now.getTime() - new Date(entry.fetchedAt).getTime();\n\n // Expired, or degraded by a transient failure → treat as a miss and re-fetch.\n if (age >= TTL_MS || isDegraded(entry)) {\n return undefined;\n }\n\n return entry;\n}\n\n/**\n * Put an enriched dependency into the cache.\n */\nexport function putEntry(\n cache: CacheFile,\n entry: CacheEntry,\n): CacheFile {\n return {\n version: CACHE_VERSION,\n entries: {\n ...cache.entries,\n [entry.enriched.name]: entry,\n },\n };\n}\n\n/**\n * Check if an entry is degraded (has transient warnings).\n */\nexport function isEntryDegraded(entry: CacheEntry): boolean {\n return isDegraded(entry);\n}\n","/**\n * Concurrency utilities for parallel data fetching.\n * @packageDocumentation\n */\n\n/**\n * Execute a function on each item, with bounded concurrency.\n * @param items - Items to process.\n * @param fn - Async function to apply.\n * @param concurrency - Maximum concurrent operations (default 8).\n * @returns Array of results, in the same order as items.\n */\nexport async function mapWithConcurrency<T, R>(\n items: readonly T[],\n fn: (item: T) => Promise<R>,\n concurrency: number = 8,\n): Promise<R[]> {\n if (items.length === 0) {\n return [];\n }\n\n // Use a mutable array to hold results\n const results: any[] = Array.from({ length: items.length });\n let index = 0;\n\n const worker = async (): Promise<void> => {\n let currentIndex = index++;\n while (currentIndex < items.length) {\n (results as any)[currentIndex] = await fn(items[currentIndex]!);\n currentIndex = index++;\n }\n };\n\n const workers = Array(Math.min(concurrency, items.length))\n .fill(null)\n .map(() => worker());\n\n await Promise.all(workers);\n return results;\n}\n","/**\n * Shared HTTP layer for all data sources.\n * @packageDocumentation\n */\n\nexport interface FetchOptions {\n readonly headers?: Record<string, string>;\n readonly timeout?: number;\n}\n\nexport type FetchOutcome<T> =\n | { status: \"ok\"; data: T }\n | { status: \"not-found\" }\n | { status: \"rate-limited\" } // 403 or 429\n | { status: \"error\"; message: string };\n\n/**\n * Fetch JSON from a URL, returning a structured outcome instead of throwing.\n * @param url - The URL to fetch.\n * @param options - Optional headers and timeout.\n * @returns A FetchOutcome indicating success, not-found, rate-limit, or error.\n */\nexport async function fetchJson<T>(\n url: string,\n options: FetchOptions = {},\n): Promise<FetchOutcome<T>> {\n const controller = new AbortController();\n const timeoutMs = options.timeout ?? 10000;\n const timeoutHandle = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetch(url, {\n headers: {\n \"user-agent\": \"rn-doctor/0.0.0\",\n ...options.headers,\n },\n signal: controller.signal,\n });\n\n clearTimeout(timeoutHandle);\n\n if (response.status === 404) {\n return { status: \"not-found\" };\n }\n\n if (response.status === 403 || response.status === 429) {\n return { status: \"rate-limited\" };\n }\n\n if (!response.ok) {\n return { status: \"error\", message: `HTTP ${response.status}` };\n }\n\n const data = (await response.json()) as T;\n return { status: \"ok\", data };\n } catch (err) {\n clearTimeout(timeoutHandle);\n\n if (err instanceof Error && err.name === \"AbortError\") {\n return { status: \"error\", message: \"Request timeout\" };\n }\n\n if (err instanceof SyntaxError) {\n return { status: \"error\", message: \"Invalid JSON response\" };\n }\n\n return {\n status: \"error\",\n message: err instanceof Error ? err.message : \"Unknown error\",\n };\n }\n}\n","/**\n * React Native Directory data source.\n * @packageDocumentation\n */\n\nimport { fetchJson, type FetchOutcome } from \"../http.js\";\nimport { mapWithConcurrency } from \"../concurrency.js\";\n\n/**\n * Result from the `/api/libraries/check` endpoint (batched check).\n */\nexport interface DirectoryCheckEntry {\n readonly unmaintained?: boolean;\n readonly newArchitecture?: \"new-arch-only\" | \"supported\" | \"unsupported\" | \"untested\";\n}\n\n/**\n * Full library entry from `/api/library` endpoint (singular lookup).\n */\nexport interface DirectoryLibraryDetail {\n readonly githubUrl?: string;\n readonly github?: {\n readonly isArchived?: boolean;\n readonly stats?: {\n readonly pushedAt?: string;\n };\n };\n readonly npm?: {\n readonly latestReleaseDate?: string;\n };\n readonly matchingScoreModifiers?: string[];\n}\n\n/**\n * Batch-check multiple packages against RN Directory in one call.\n * Packages not in the directory are simply omitted from the response.\n * @param packageNames - List of npm package names (may be scoped).\n * @returns A map of package name → check result (absent keys = not listed).\n */\nexport async function checkLibraries(\n packageNames: readonly string[],\n): Promise<FetchOutcome<Record<string, DirectoryCheckEntry>>> {\n if (packageNames.length === 0) {\n return { status: \"ok\", data: {} };\n }\n\n // Chunk names into ~200-name batches to stay well under URL length limits\n const chunks: string[][] = [];\n for (let i = 0; i < packageNames.length; i += 200) {\n chunks.push(Array.from(packageNames.slice(i, i + 200)));\n }\n\n const results: Record<string, DirectoryCheckEntry> = {};\n\n for (const chunk of chunks) {\n const url = new URL(\"https://reactnative.directory/api/libraries/check\");\n url.searchParams.set(\"packages\", chunk.join(\",\"));\n\n const outcome = await fetchJson<Record<string, DirectoryCheckEntry>>(url.toString());\n\n if (outcome.status !== \"ok\") {\n return outcome;\n }\n\n Object.assign(results, outcome.data);\n }\n\n return { status: \"ok\", data: results };\n}\n\n/**\n * Fetch full library details for a single package from RN Directory.\n * Unknown packages return an empty object (not an error).\n * @param packageName - The npm package name.\n * @returns The library detail or an empty object if not found.\n */\nexport async function fetchLibraryDetail(\n packageName: string,\n): Promise<FetchOutcome<DirectoryLibraryDetail>> {\n const url = new URL(\"https://reactnative.directory/api/library\");\n url.searchParams.set(\"name\", packageName);\n\n const outcome = await fetchJson<DirectoryLibraryDetail>(url.toString());\n\n if (outcome.status !== \"ok\") {\n return outcome;\n }\n\n // Empty object `{}` means not found\n if (Object.keys(outcome.data).length === 0) {\n return { status: \"ok\", data: {} };\n }\n\n return outcome;\n}\n\n/**\n * Batch-fetch library details for multiple packages.\n * Not listed packages result in empty objects.\n * @param packageNames - List of npm package names to fetch details for.\n * @param concurrency - Maximum concurrent requests (default 8).\n * @returns A map of package name → library detail (or empty object if not found).\n */\nexport async function fetchLibraryDetails(\n packageNames: readonly string[],\n concurrency = 8,\n): Promise<Record<string, DirectoryLibraryDetail>> {\n const outcomes: any[] = await mapWithConcurrency(\n packageNames,\n (name) => fetchLibraryDetail(name),\n concurrency,\n );\n\n const results: Record<string, DirectoryLibraryDetail> = {};\n\n for (let i = 0; i < outcomes.length; i++) {\n const outcome = (outcomes as any)[i];\n if (outcome?.status === \"ok\") {\n results[(packageNames as any)[i]] = outcome.data;\n }\n }\n\n return results;\n}\n","/**\n * npm registry data source.\n * @packageDocumentation\n */\n\nimport { fetchJson, type FetchOutcome } from \"../http.js\";\n\n/**\n * Version object from npm registry `/latest` endpoint.\n */\nexport interface NpmVersionManifest {\n readonly name: string;\n readonly version: string;\n readonly deprecated?: string;\n readonly codegenConfig?: unknown;\n readonly peerDependencies?: Record<string, string>;\n readonly dependencies?: Record<string, string>;\n readonly repository?: { readonly type?: string; readonly url?: string };\n readonly files?: string[];\n}\n\n/**\n * Search result from npm search API.\n */\nexport interface NpmSearchResult {\n readonly objects?: Array<{\n readonly package: {\n readonly name: string;\n readonly version: string;\n readonly date: string;\n };\n }>;\n}\n\n/**\n * Fetch the latest version manifest from npm registry.\n * @param packageName - The npm package name (may be scoped, e.g. `@react-native-community/netinfo`).\n * @returns The version manifest or an outcome describing what went wrong.\n */\nexport async function fetchNpmLatestManifest(\n packageName: string,\n): Promise<FetchOutcome<NpmVersionManifest>> {\n const encoded = encodeURIComponent(packageName);\n const url = `https://registry.npmjs.org/${encoded}/latest`;\n\n return fetchJson<NpmVersionManifest>(url);\n}\n\n/**\n * Search npm for a package and return the top result's publish date.\n * Callers must verify that the returned name matches exactly; if not, treat as not-found.\n * @param packageName - The npm package name to search for.\n * @returns The search result or an outcome describing what went wrong.\n */\nexport async function searchNpmForPackage(\n packageName: string,\n): Promise<FetchOutcome<{ readonly name: string; readonly date: string }>> {\n const url = new URL(\"https://registry.npmjs.org/-/v1/search\");\n url.searchParams.set(\"text\", packageName);\n url.searchParams.set(\"size\", \"1\");\n\n const outcome = await fetchJson<NpmSearchResult>(url.toString());\n\n if (outcome.status !== \"ok\") {\n return outcome;\n }\n\n const topResult = outcome.data.objects?.[0];\n if (!topResult) {\n return { status: \"error\", message: \"No search results\" };\n }\n\n return {\n status: \"ok\",\n data: {\n name: topResult.package.name,\n date: topResult.package.date,\n },\n };\n}\n\n/**\n * Parse a repository URL to extract owner and repo for GitHub (if applicable).\n * @param repoUrl - A repository URL, e.g. `git+https://github.com/owner/repo.git`.\n * @returns `{ owner, repo }` if it's a GitHub URL, or `undefined` otherwise.\n */\nexport function parseGithubUrl(\n repoUrl: string | undefined,\n): { readonly owner: string; readonly repo: string } | undefined {\n if (!repoUrl) {\n return undefined;\n }\n\n // Match patterns like:\n // git+https://github.com/owner/repo.git\n // https://github.com/owner/repo.git\n // github.com/owner/repo\n const match = repoUrl.match(/github\\.com[/:]([\\w.-]+)\\/([\\w.-]+?)(?:\\.git)?$/i);\n if (!match || !match[1] || !match[2]) {\n return undefined;\n }\n\n return { owner: match[1], repo: match[2] };\n}\n","/**\n * GitHub API data source with rate-limit circuit breaker.\n * @packageDocumentation\n */\n\nimport { fetchJson, type FetchOutcome } from \"../http.js\";\n\n/**\n * Repository metadata from GitHub API.\n */\nexport interface GithubRepoInfo {\n readonly archived?: boolean;\n readonly pushed_at?: string;\n}\n\n/**\n * Circuit breaker state for GitHub API rate limiting.\n */\nexport class GitHubCircuitBreaker {\n private tripped = false;\n\n /**\n * Check if the circuit breaker is currently tripped.\n */\n isTripped(): boolean {\n return this.tripped;\n }\n\n /**\n * Trip the circuit breaker (call this on a 403/429 response).\n */\n trip(): void {\n this.tripped = true;\n }\n}\n\n/**\n * Fetch repository metadata from GitHub API.\n * @param owner - Repository owner (e.g. \"facebook\").\n * @param repo - Repository name (e.g. \"react-native\").\n * @param token - Optional GitHub API token for higher rate limits.\n * @returns Repository info or an outcome describing what went wrong.\n */\nexport async function fetchGithubRepo(\n owner: string,\n repo: string,\n token?: string,\n): Promise<FetchOutcome<GithubRepoInfo>> {\n const url = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`;\n\n const headers: Record<string, string> = {};\n if (token) {\n headers[\"authorization\"] = `Bearer ${token}`;\n }\n\n return fetchJson<GithubRepoInfo>(url, { headers });\n}\n\n/**\n * Parse a GitHub repository URL to extract owner and repo.\n * @param url - A GitHub URL like `https://github.com/owner/repo` or `git@github.com:owner/repo.git`.\n * @returns `{ owner, repo }` if parseable as GitHub, or `undefined` otherwise.\n */\nexport function parseGithubUrl(\n url: string | undefined,\n): { readonly owner: string; readonly repo: string } | undefined {\n if (!url) {\n return undefined;\n }\n\n // Match patterns:\n // https://github.com/owner/repo\n // https://github.com/owner/repo.git\n // https://github.com/owner/repo/tree/...\n // git@github.com:owner/repo.git\n const match = url.match(\n /(?:https:\\/\\/github\\.com|git@github\\.com:)\\/?([^/]+)\\/([^/.]+)(?:\\.git|\\/|$)/i,\n );\n\n if (!match || !match[1] || !match[2]) {\n return undefined;\n }\n\n return { owner: match[1], repo: match[2] };\n}\n","/**\n * Enrichment engine orchestrator.\n * Coordinates data gathering from npm, RN Directory, and GitHub.\n * @packageDocumentation\n */\n\nimport type {\n EnrichedDependency,\n EnrichmentOptions,\n EnrichmentResult,\n EnrichmentWarning,\n NewArchTier,\n Signal,\n UnknownReason,\n} from \"./types.js\";\nimport { readCacheFile, writeCacheFile, getFreshEntry, putEntry, isEntryDegraded } from \"./cache.js\";\nimport { mapWithConcurrency } from \"./concurrency.js\";\nimport { checkLibraries, fetchLibraryDetails } from \"./sources/directory.js\";\nimport { fetchNpmLatestManifest, searchNpmForPackage, parseGithubUrl as parseGithubUrlNpm } from \"./sources/npm.js\";\nimport { fetchGithubRepo, GitHubCircuitBreaker, parseGithubUrl as parseGithubUrlGithub } from \"./sources/github.js\";\n\n/**\n * Compute the RN-native detection boolean for a dependency.\n */\nexport function computeIsRnNative(dep: EnrichedDependency): boolean {\n return (\n dep.directory.listed ||\n (dep.npm.hasReactNativePeerDep.known && dep.npm.hasReactNativePeerDep.value) ||\n (dep.npm.hasNativeDirsHint.known && dep.npm.hasNativeDirsHint.value)\n );\n}\n\n/**\n * Compute the New Architecture tier for a dependency.\n */\nexport function computeNewArchTier(dep: {\n directoryVerdict: string | null;\n hasCodegenConfig: { known: boolean; value?: boolean };\n}): NewArchTier {\n const { directoryVerdict, hasCodegenConfig } = dep;\n\n if (directoryVerdict === \"supported\" || directoryVerdict === \"new-arch-only\") {\n return \"supported\";\n }\n\n if (directoryVerdict === \"unsupported\") {\n return \"unsupported\";\n }\n\n // untested or null (not in directory) + has codegenConfig → pass with note\n if (hasCodegenConfig.known && hasCodegenConfig.value) {\n return \"passWithNote\";\n }\n\n return \"unknown\";\n}\n\n/**\n * Enrich multiple dependencies from npm, RN Directory, and GitHub.\n */\nexport async function enrichDependencies(\n names: readonly string[],\n options: EnrichmentOptions = {},\n): Promise<EnrichmentResult> {\n const cacheDir = options.cacheDir || process.cwd();\n const concurrency = options.concurrency ?? 8;\n const githubToken = options.githubToken || process.env.GITHUB_TOKEN;\n\n const warnings: EnrichmentWarning[] = [];\n const enriched: EnrichedDependency[] = [];\n\n // Phase 0: Load and split cache\n let cache = { version: 1 as const, entries: {} };\n\n if (!options.noCache) {\n try {\n cache = await readCacheFile(cacheDir);\n } catch {\n warnings.push({\n source: \"cache\",\n message: \"Failed to read cache file; proceeding without cache\",\n });\n }\n }\n\n const cached: EnrichedDependency[] = [];\n const toFetch: string[] = [];\n\n for (const name of names) {\n const entry = options.noCache ? undefined : getFreshEntry(cache, name);\n if (entry) {\n cached.push(entry.enriched);\n } else {\n toFetch.push(name);\n }\n }\n\n // Add cached results immediately\n enriched.push(...cached);\n\n if (toFetch.length === 0) {\n return { dependencies: enriched, warnings };\n }\n\n // Phase 1: Batch directory check\n const directoryCheckOutcome = await checkLibraries(toFetch);\n const directoryCheckData: Record<string, any> = (directoryCheckOutcome.status === \"ok\"\n ? directoryCheckOutcome.data\n : {}) as Record<string, any>;\n\n if (directoryCheckOutcome.status === \"error\") {\n warnings.push({\n source: \"directory\",\n message: `Failed to check RN Directory: ${directoryCheckOutcome.message}`,\n });\n }\n\n // Phase 2: Fetch npm /latest for all packages needing fetch\n const npmManifests = await mapWithConcurrency(\n toFetch,\n async (name) => {\n const outcome = await fetchNpmLatestManifest(name);\n return { name, outcome };\n },\n concurrency,\n );\n\n const npmData: Record<string, any> = {};\n for (const { name, outcome } of npmManifests) {\n if (outcome.status === \"ok\") {\n (npmData as any)[name] = outcome.data;\n } else if (outcome.status === \"not-found\") {\n (npmData as any)[name] = { found: false };\n } else if (outcome.status === \"error\") {\n (npmData as any)[name] = { error: outcome.message };\n } else if (outcome.status === \"rate-limited\") {\n (npmData as any)[name] = { rateLimited: true };\n }\n }\n\n // Phase 3: Fetch RN Directory details for directory-listed packages\n const dirListed = toFetch.filter((name) => directoryCheckData[name]);\n const directoryDetails = await (dirListed.length > 0 ? fetchLibraryDetails(dirListed, concurrency) : Promise.resolve({}));\n\n // Phase 4: Fetch npm search for non-directory-listed packages\n const dirNotListed = toFetch.filter((name) => !directoryCheckData[name]);\n const npmSearchResults: Record<string, { name: string; date: string } | undefined> = {};\n\n for (const name of dirNotListed) {\n const outcome = await searchNpmForPackage(name);\n if (outcome.status === \"ok\" && outcome.data.name === name) {\n npmSearchResults[name] = outcome.data;\n }\n }\n\n // Phase 5: Resolve GitHub URLs and prepare for GitHub calls\n const githubBreaker = new GitHubCircuitBreaker();\n const githubCalls: Array<{\n name: string;\n owner: string;\n repo: string;\n }> = [];\n // Track which packages had a resolvable repo URL, so the \"no repo at all\" case\n // reports \"no-repo-url\" rather than the misleading \"no-github-token\".\n const packagesWithRepoUrl = new Set<string>();\n\n for (const name of toFetch) {\n let repoUrl: string | undefined;\n\n // Try directory first\n const dirDetail = (directoryDetails as any)[name];\n if (dirDetail && typeof dirDetail === \"object\" && \"githubUrl\" in dirDetail) {\n repoUrl = (dirDetail as any).githubUrl;\n }\n\n // Fallback to npm\n if (!repoUrl && (npmData as any)[name]?.repository?.url) {\n repoUrl = (npmData as any)[name].repository.url;\n }\n\n if (!repoUrl) {\n continue;\n }\n\n packagesWithRepoUrl.add(name);\n\n // Try to parse as GitHub\n let parsed = parseGithubUrlGithub(repoUrl);\n if (!parsed) {\n parsed = parseGithubUrlNpm(repoUrl);\n }\n\n if (parsed) {\n githubCalls.push({ name, owner: parsed.owner, repo: parsed.repo });\n }\n }\n\n // Phase 5b: Fetch GitHub data with circuit breaker\n const githubData: Record<string, any> = {};\n\n for (const { name, owner, repo } of githubCalls) {\n if (githubBreaker.isTripped()) {\n break;\n }\n\n const outcome = await fetchGithubRepo(owner, repo, githubToken);\n\n if (outcome.status === \"ok\") {\n githubData[name] = outcome.data;\n } else if (outcome.status === \"rate-limited\") {\n githubBreaker.trip();\n warnings.push({\n source: \"github\",\n message: \"GitHub API rate-limited after checking dependencies; remaining packages fall back to cached GitHub data or unknown\",\n });\n break;\n } else if (outcome.status === \"error\") {\n (githubData as any)[name] = { error: outcome.message };\n }\n }\n\n // Phase 6: Assemble enriched dependencies\n for (const name of toFetch) {\n const npm = (npmData as any)[name] || {};\n const dirCheck = (directoryCheckData as any)[name] || {};\n // `dirCheck` is `{}` for unlisted packages, and `Boolean({})` is truthy — so listing\n // must be decided by presence of the key, not the object's truthiness.\n const listedInDirectory = Object.prototype.hasOwnProperty.call(directoryCheckData, name);\n const dirDetail = (directoryDetails as any)[name] || {};\n const github = (githubData as any)[name];\n\n // Determine npm status\n const npmFound = npm.found !== false && !npm.rateLimited && !npm.error;\n const npmNotFound = npm.found === false;\n\n // Build npm signals\n const deprecated: Signal<{ deprecated: boolean; message: string | null }> = npmFound\n ? {\n known: true,\n value: {\n deprecated: Boolean(npm.deprecated),\n message: npm.deprecated ? String(npm.deprecated) : null,\n },\n source: \"npm\",\n }\n : { known: false, reason: npmNotFound ? \"npm-not-found\" : \"npm-lookup-failed\" };\n\n const hasCodegenConfig: Signal<boolean> = npmFound\n ? { known: true, value: Boolean(npm.codegenConfig), source: \"npm\" }\n : { known: false, reason: npmNotFound ? \"npm-not-found\" : \"npm-lookup-failed\" };\n\n const hasReactNativePeerDep: Signal<boolean> = npmFound\n ? {\n known: true,\n value: Boolean(npm.peerDependencies?.[\"react-native\"]),\n source: \"npm\",\n }\n : { known: false, reason: npmNotFound ? \"npm-not-found\" : \"npm-lookup-failed\" };\n\n // Check for native dirs in files array\n const hasNativeDirsHint: Signal<boolean> = npmFound\n ? {\n known: true,\n value: Boolean(\n npm.files && Array.isArray(npm.files) && npm.files.some((f: string) => /^(android|ios)(\\/.*)?$/.test(f)),\n ),\n source: \"npm\",\n }\n : { known: false, reason: npmNotFound ? \"npm-not-found\" : \"npm-lookup-failed\" };\n\n // GitHub signals with fallback to directory\n let archivedSignal: Signal<boolean>;\n let pushedAtSignal: Signal<string>;\n let githubSource: \"github-api\" | \"directory-fallback\" | null = null;\n\n if (github && !github.error) {\n archivedSignal = { known: true, value: github.archived === true, source: \"github-api\" };\n if (github.pushed_at) {\n pushedAtSignal = { known: true, value: github.pushed_at, source: \"github-api\" };\n } else {\n pushedAtSignal = { known: false, reason: \"github-error\" };\n }\n githubSource = \"github-api\";\n } else if (dirDetail && typeof dirDetail === \"object\" && \"github\" in dirDetail) {\n const dir = dirDetail as any;\n if (dir.github?.isArchived !== undefined) {\n archivedSignal = { known: true, value: dir.github.isArchived, source: \"directory-fallback\" };\n if (dir.github.stats?.pushedAt) {\n pushedAtSignal = { known: true, value: dir.github.stats.pushedAt, source: \"directory-fallback\" };\n } else {\n pushedAtSignal = { known: false, reason: \"github-error\" };\n }\n githubSource = \"directory-fallback\";\n } else {\n archivedSignal = { known: false, reason: githubBreaker.isTripped() ? \"github-rate-limited\" : \"no-github-token\" };\n pushedAtSignal = { known: false, reason: githubBreaker.isTripped() ? \"github-rate-limited\" : \"no-github-token\" };\n }\n } else {\n const noGithubReason: UnknownReason = githubBreaker.isTripped()\n ? \"github-rate-limited\"\n : packagesWithRepoUrl.has(name)\n ? \"no-github-token\"\n : \"no-repo-url\";\n archivedSignal = { known: false, reason: noGithubReason };\n pushedAtSignal = { known: false, reason: noGithubReason };\n }\n\n // Resolve github URL\n let githubUrl: string | null = null;\n if (dirDetail && typeof dirDetail === \"object\" && \"githubUrl\" in dirDetail) {\n githubUrl = (dirDetail as any).githubUrl || null;\n } else if (npm.repository?.url) {\n const parsed = parseGithubUrlNpm(npm.repository.url) || parseGithubUrlGithub(npm.repository.url);\n if (parsed) {\n githubUrl = `https://github.com/${parsed.owner}/${parsed.repo}`;\n }\n }\n\n // Last publish signal\n let lastPublish: Signal<{ date: string }>;\n\n if (dirDetail && typeof dirDetail === \"object\" && \"npm\" in dirDetail) {\n const dir = dirDetail as any;\n if (dir.npm?.latestReleaseDate) {\n lastPublish = { known: true, value: { date: dir.npm.latestReleaseDate }, source: \"directory\" };\n } else if (npmSearchResults[name]?.date) {\n lastPublish = { known: true, value: { date: npmSearchResults[name].date }, source: \"npm-search\" };\n } else {\n lastPublish = { known: false, reason: \"not-in-directory\" };\n }\n } else if (npmSearchResults[name]?.date) {\n lastPublish = { known: true, value: { date: npmSearchResults[name].date }, source: \"npm-search\" };\n } else {\n lastPublish = { known: false, reason: \"not-in-directory\" };\n }\n\n // Build rnNativeReasons\n const rnNativeReasons: (\"directory-listed\" | \"peer-dependency\" | \"native-files-hint\")[] = [];\n if (listedInDirectory) {\n rnNativeReasons.push(\"directory-listed\");\n }\n if (hasReactNativePeerDep.known && hasReactNativePeerDep.value) {\n rnNativeReasons.push(\"peer-dependency\");\n }\n if (hasNativeDirsHint.known && hasNativeDirsHint.value) {\n rnNativeReasons.push(\"native-files-hint\");\n }\n\n const isRnNative = listedInDirectory || (hasReactNativePeerDep.known && hasReactNativePeerDep.value) ||\n (hasNativeDirsHint.known && hasNativeDirsHint.value);\n\n // Compute newArch tier\n const newArchTier = computeNewArchTier({\n directoryVerdict: (dirCheck.newArchitecture as any) || null,\n hasCodegenConfig,\n });\n\n // Build warnings\n const depWarnings: EnrichmentWarning[] = [];\n if (npmNotFound) {\n depWarnings.push({\n source: \"npm\",\n dependency: name,\n message: `Package not found on npm registry — verify the package name is correct`,\n });\n }\n if (npm.error) {\n depWarnings.push({\n source: \"npm\",\n dependency: name,\n message: `Failed to fetch npm data: ${npm.error}`,\n });\n }\n if (github?.error) {\n depWarnings.push({\n source: \"github\",\n dependency: name,\n message: `Failed to fetch GitHub data: ${github.error}`,\n });\n }\n\n const enrichedDep: EnrichedDependency = {\n name,\n warnings: depWarnings,\n npm: {\n found: npmFound && !npmNotFound,\n latestVersion: npmFound ? npm.version : null,\n deprecated,\n hasCodegenConfig,\n hasReactNativePeerDep,\n hasNativeDirsHint,\n repositoryUrl: npm.repository?.url ? String(npm.repository.url).replace(/^git\\+/, \"\") : null,\n },\n directory: {\n listed: listedInDirectory,\n unmaintained: Boolean(dirCheck.unmaintained),\n newArchitectureRaw: (dirCheck.newArchitecture as any) || null,\n // Directory-sourced URL only; the npm-derived fallback belongs to `github.repoUrl`.\n githubUrl: dirDetail && typeof dirDetail === \"object\" ? (dirDetail as any).githubUrl || null : null,\n lastPublishedAt:\n (dirDetail && typeof dirDetail === \"object\" && (dirDetail as any).npm?.latestReleaseDate) || null,\n // `?? null` (not `|| null`) so a genuine `false` (repo not archived) survives.\n githubArchived:\n dirDetail && typeof dirDetail === \"object\" && typeof (dirDetail as any).github?.isArchived === \"boolean\"\n ? (dirDetail as any).github.isArchived\n : null,\n githubPushedAt:\n (dirDetail && typeof dirDetail === \"object\" && (dirDetail as any).github?.stats?.pushedAt) || null,\n matchingScoreModifiers:\n (dirDetail && typeof dirDetail === \"object\" && (dirDetail as any).matchingScoreModifiers) || [],\n },\n github: {\n archived: archivedSignal,\n pushedAt: pushedAtSignal,\n repoUrl: githubUrl,\n source: githubSource,\n },\n isRnNative,\n rnNativeReasons,\n newArch: {\n tier: newArchTier,\n evidence: {\n directoryVerdict: (dirCheck.newArchitecture as any) || null,\n hasCodegenConfig: hasCodegenConfig.known ? hasCodegenConfig.value : null,\n },\n },\n lastPublish,\n };\n\n enriched.push(enrichedDep);\n\n // Write to cache — but never persist a degraded (transient-failure) entry, so a\n // momentary rate-limit or network blip can't poison future runs with stale unknowns.\n if (!options.noCache) {\n const candidate = { fetchedAt: new Date().toISOString(), enriched: enrichedDep };\n if (!isEntryDegraded(candidate)) {\n cache = putEntry(cache, candidate);\n }\n }\n }\n\n // Write cache back to disk\n if (!options.noCache && toFetch.length > 0) {\n try {\n await writeCacheFile(cacheDir, cache);\n } catch {\n warnings.push({\n source: \"cache\",\n message: \"Failed to write cache file\",\n });\n }\n }\n\n return { dependencies: enriched, warnings };\n}\n","/**\n * Policy engine: turns enriched dependency records into findings.\n *\n * Pure and deterministic — no I/O, no clock reads unless the caller omits\n * `options.now`. Reporters (Phase 3) consume the `Finding[]` output.\n *\n * @packageDocumentation\n */\n\nimport type { EnrichedDependency } from \"./types.js\";\n\n/**\n * Identifier of a policy rule; also the `rule` field on emitted findings.\n */\nexport type RuleId =\n | \"newArchitecture\"\n | \"newArchUnknown\"\n | \"lastPublish\"\n | \"githubArchived\"\n | \"npmDeprecated\"\n | \"directoryUnmaintained\";\n\n/**\n * Severity a rule can be configured to fire at. `off` disables the rule.\n */\nexport type RuleSeverity = \"error\" | \"warn\" | \"off\";\n\n/**\n * Staleness thresholds for the `lastPublish` rule, in months since the\n * latest npm publish. `errorMonths` should be greater than `warnMonths`.\n */\nexport interface LastPublishThresholds {\n readonly warnMonths: number;\n readonly errorMonths: number;\n}\n\n/**\n * Which dependencies the policy applies to: only those detected as React\n * Native native modules, or every dependency.\n */\nexport type PolicyScope = \"rn-native-only\" | \"all-deps\";\n\n/**\n * One allowlist entry: suppresses findings for `package` until `expires`.\n * An expired entry escalates the findings it used to suppress to `error`.\n */\nexport interface AllowEntry {\n /** Exact npm package name the entry applies to. */\n readonly package: string;\n /** Why the package is allowed (shown in reports). */\n readonly reason: string | null;\n /** `YYYY-MM-DD` (inclusive, UTC). `null` means the entry never expires. */\n readonly expires: string | null;\n}\n\n/**\n * Per-rule severity configuration.\n */\nexport interface PolicyRules {\n /** How to treat a package the RN Directory marks New-Architecture-unsupported. */\n readonly newArchitecture: RuleSeverity;\n /** How to treat a package whose New Architecture support cannot be determined. */\n readonly newArchUnknown: RuleSeverity;\n /** Staleness thresholds for the latest npm publish, or `off`. */\n readonly lastPublish: LastPublishThresholds | \"off\";\n /** How to treat a package whose GitHub repository is archived. */\n readonly githubArchived: RuleSeverity;\n /** How to treat a package deprecated on npm. */\n readonly npmDeprecated: RuleSeverity;\n /** How to treat a package the RN Directory flags as unmaintained. */\n readonly directoryUnmaintained: RuleSeverity;\n}\n\n/**\n * A complete policy: rule severities, scope, and the allowlist.\n */\nexport interface Policy {\n readonly rules: PolicyRules;\n readonly scope: PolicyScope;\n readonly allow: readonly AllowEntry[];\n}\n\n/**\n * The default policy, used when no `.rn-doctor.yml` is present.\n * Matches the documented sample: hard failures for clearly-dead signals,\n * warnings where data is merely missing (honest, not alarmist).\n */\nexport const DEFAULT_POLICY: Policy = {\n rules: {\n newArchitecture: \"error\",\n newArchUnknown: \"warn\",\n lastPublish: { warnMonths: 12, errorMonths: 24 },\n githubArchived: \"error\",\n npmDeprecated: \"error\",\n directoryUnmaintained: \"warn\",\n },\n scope: \"rn-native-only\",\n allow: [],\n};\n\n/**\n * Severity of an emitted finding. `note` is informational — it never fails\n * the run and is not subject to allowlisting.\n */\nexport type FindingSeverity = \"error\" | \"warn\" | \"note\";\n\n/**\n * A single policy violation (or informational note) for one dependency.\n */\nexport interface Finding {\n /** npm package name the finding is about. */\n readonly package: string;\n /** The rule that produced the finding. */\n readonly rule: RuleId;\n /** Effective severity after allowlist processing. */\n readonly severity: FindingSeverity;\n /** Human-readable verdict + reason + what to do about it. */\n readonly message: string;\n /** Link to the evidence backing the verdict, when one exists. */\n readonly evidenceUrl: string | null;\n /**\n * Set when an active allowlist entry suppresses this finding. Suppressed\n * findings keep their severity for display but must not fail the run.\n */\n readonly suppressedBy: { readonly reason: string | null; readonly expires: string | null } | null;\n}\n\n/**\n * Options for {@link evaluatePolicy}.\n */\nexport interface EvaluateOptions {\n /** Clock used for staleness and allowlist expiry. Defaults to `new Date()`. */\n readonly now?: Date;\n}\n\n/** Mean Gregorian month in milliseconds; used for publish-age thresholds. */\nconst MEAN_MONTH_MS = (365.2425 / 12) * 24 * 60 * 60 * 1000;\n\n/** How an allowlist entry applies to a package's findings. */\ntype AllowState =\n | { readonly kind: \"none\" }\n | { readonly kind: \"active\"; readonly entry: AllowEntry }\n | { readonly kind: \"expired\"; readonly entry: AllowEntry };\n\nfunction allowStateFor(name: string, allow: readonly AllowEntry[], now: Date): AllowState {\n const entry = allow.find((a) => a.package === name);\n if (!entry) return { kind: \"none\" };\n if (entry.expires === null) return { kind: \"active\", entry };\n // The expiry day is inclusive in UTC: the entry stops applying the next day.\n const expiryEnd = Date.parse(`${entry.expires}T23:59:59.999Z`);\n return now.getTime() <= expiryEnd ? { kind: \"active\", entry } : { kind: \"expired\", entry };\n}\n\nfunction allowHint(name: string): string {\n return (\n `If this is intentional, allowlist it in .rn-doctor.yml: ` +\n `{ package: ${name}, reason: \"<why>\", expires: YYYY-MM-DD }.`\n );\n}\n\nfunction directoryUrl(name: string): string {\n return `https://reactnative.directory/?search=${encodeURIComponent(name)}`;\n}\n\nfunction npmUrl(name: string): string {\n return `https://www.npmjs.com/package/${name}`;\n}\n\n/**\n * A raw rule hit before allowlist processing.\n */\ninterface RuleHit {\n readonly rule: RuleId;\n readonly severity: FindingSeverity;\n readonly message: string;\n readonly evidenceUrl: string | null;\n}\n\nfunction evaluateRules(dep: EnrichedDependency, rules: PolicyRules, now: Date): RuleHit[] {\n const hits: RuleHit[] = [];\n const name = dep.name;\n\n // newArchitecture — directory says \"unsupported\", or unknown-with-codegen note.\n if (rules.newArchitecture !== \"off\") {\n if (dep.newArch.tier === \"unsupported\") {\n hits.push({\n rule: \"newArchitecture\",\n severity: rules.newArchitecture,\n message:\n `${name} does not support the React Native New Architecture ` +\n `(RN Directory verdict: unsupported). Look for a maintained alternative ` +\n `or check the repo for New Architecture plans. ${allowHint(name)}`,\n evidenceUrl: directoryUrl(name),\n });\n } else if (dep.newArch.tier === \"passWithNote\") {\n hits.push({\n rule: \"newArchitecture\",\n severity: \"note\",\n message:\n `${name} is not listed in the RN Directory, but its npm package ships a ` +\n `codegenConfig, which indicates New Architecture support. Treating as a pass; ` +\n `verify manually if this dependency is critical.`,\n evidenceUrl: npmUrl(name),\n });\n }\n }\n\n // newArchUnknown — no directory verdict and no codegen hint.\n if (rules.newArchUnknown !== \"off\" && dep.newArch.tier === \"unknown\") {\n hits.push({\n rule: \"newArchUnknown\",\n severity: rules.newArchUnknown,\n message:\n `New Architecture support for ${name} is unknown: it is not listed in the ` +\n `RN Directory and its npm package has no codegen hints. Verify support ` +\n `manually (check the repo README/releases). ${allowHint(name)}`,\n evidenceUrl: directoryUrl(name),\n });\n }\n\n // lastPublish — staleness of the latest npm publish.\n if (rules.lastPublish !== \"off\" && dep.lastPublish.known) {\n const publishedAt = Date.parse(dep.lastPublish.value.date);\n if (!Number.isNaN(publishedAt)) {\n const ageMonths = (now.getTime() - publishedAt) / MEAN_MONTH_MS;\n const { warnMonths, errorMonths } = rules.lastPublish;\n const band: FindingSeverity | null =\n ageMonths >= errorMonths ? \"error\" : ageMonths >= warnMonths ? \"warn\" : null;\n if (band !== null) {\n const threshold = band === \"error\" ? errorMonths : warnMonths;\n hits.push({\n rule: \"lastPublish\",\n severity: band,\n message:\n `${name} was last published ${String(Math.floor(ageMonths))} months ago ` +\n `(${dep.lastPublish.value.date.slice(0, 10)}), exceeding the ` +\n `${String(threshold)}-month threshold. Check whether it is abandoned; ` +\n `consider a maintained fork, or let renovate surface a replacement. ` +\n allowHint(name),\n evidenceUrl: npmUrl(name),\n });\n }\n }\n }\n\n // githubArchived — the repository is read-only.\n if (rules.githubArchived !== \"off\" && dep.github.archived.known && dep.github.archived.value) {\n hits.push({\n rule: \"githubArchived\",\n severity: rules.githubArchived,\n message:\n `The GitHub repository for ${name} is archived (read-only): no fixes or ` +\n `releases will land. Replace it or pin a fork. ${allowHint(name)}`,\n evidenceUrl: dep.github.repoUrl ?? npmUrl(name),\n });\n }\n\n // npmDeprecated — the package owner marked it deprecated.\n if (rules.npmDeprecated !== \"off\" && dep.npm.deprecated.known && dep.npm.deprecated.value.deprecated) {\n const upstream = dep.npm.deprecated.value.message;\n hits.push({\n rule: \"npmDeprecated\",\n severity: rules.npmDeprecated,\n message:\n `${name} is deprecated on npm` +\n (upstream ? `: \"${upstream}\"` : \"\") +\n `. Follow the deprecation notice to migrate. ${allowHint(name)}`,\n evidenceUrl: npmUrl(name),\n });\n }\n\n // directoryUnmaintained — RN Directory's own unmaintained flag.\n if (rules.directoryUnmaintained !== \"off\" && dep.directory.listed && dep.directory.unmaintained) {\n hits.push({\n rule: \"directoryUnmaintained\",\n severity: rules.directoryUnmaintained,\n message:\n `${name} is flagged as unmaintained in the RN Directory. Plan a migration ` +\n `to a maintained alternative. ${allowHint(name)}`,\n evidenceUrl: directoryUrl(name),\n });\n }\n\n return hits;\n}\n\n/**\n * Evaluate a policy against enriched dependencies and produce findings.\n *\n * @remarks\n * Pure: same inputs (including `options.now`) always yield the same output,\n * in a stable order (input dependency order, then a fixed rule order).\n *\n * Allowlist semantics: an **active** entry keeps the finding visible but sets\n * {@link Finding.suppressedBy} (reporters must not fail the run on suppressed\n * findings); an **expired** entry escalates the finding to `error` and says\n * so in the message. `note` findings are informational and unaffected.\n *\n * @param dependencies - Enriched records from the enrichment engine.\n * @param policy - The effective policy (see {@link DEFAULT_POLICY}).\n * @param options - Evaluation options; inject `now` for deterministic output.\n * @returns All findings, unfiltered — including suppressed ones.\n */\nexport function evaluatePolicy(\n dependencies: readonly EnrichedDependency[],\n policy: Policy,\n options: EvaluateOptions = {},\n): readonly Finding[] {\n const now = options.now ?? new Date();\n const findings: Finding[] = [];\n\n for (const dep of dependencies) {\n if (policy.scope === \"rn-native-only\" && !dep.isRnNative) continue;\n\n const allowState = allowStateFor(dep.name, policy.allow, now);\n\n for (const hit of evaluateRules(dep, policy.rules, now)) {\n if (hit.severity === \"note\" || allowState.kind === \"none\") {\n findings.push({ package: dep.name, ...hit, suppressedBy: null });\n } else if (allowState.kind === \"active\") {\n findings.push({\n package: dep.name,\n ...hit,\n suppressedBy: {\n reason: allowState.entry.reason,\n expires: allowState.entry.expires,\n },\n });\n } else {\n // Expired allow: the grace period is over — escalate to error.\n const { entry } = allowState;\n findings.push({\n package: dep.name,\n rule: hit.rule,\n severity: \"error\",\n message:\n `${hit.message} NOTE: the allowlist entry for ${dep.name}` +\n (entry.reason ? ` (\"${entry.reason}\")` : \"\") +\n ` expired on ${entry.expires ?? \"?\"} — escalated to error. ` +\n `Renew the entry with a new expiry, or remove the dependency.`,\n evidenceUrl: hit.evidenceUrl,\n suppressedBy: null,\n });\n }\n }\n }\n\n return findings;\n}\n","/**\n * `.rn-doctor.yml` loading and validation.\n *\n * Parses the policy file with `yaml`, validates the untyped result with\n * explicit type guards (typo protection: unknown keys and bad values are\n * rejected with actionable messages), and merges partial user config over\n * {@link DEFAULT_POLICY}.\n *\n * @packageDocumentation\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { parse } from \"yaml\";\n\nimport type { AllowEntry, Policy, PolicyRules, PolicyScope, RuleSeverity } from \"./policy.js\";\nimport { DEFAULT_POLICY } from \"./policy.js\";\n\n/** The policy file name looked up in the working directory by default. */\nexport const DEFAULT_POLICY_FILENAME = \".rn-doctor.yml\";\n\n/**\n * A policy file could not be read or is invalid. Maps to exit code 2\n * (tool failure) in the CLI — a broken policy must never silently pass.\n */\nexport class PolicyError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"PolicyError\";\n }\n}\n\nconst RULE_SEVERITIES: readonly RuleSeverity[] = [\"error\", \"warn\", \"off\"];\nconst SCOPES: readonly PolicyScope[] = [\"rn-native-only\", \"all-deps\"];\nconst RULE_KEYS: readonly (keyof PolicyRules)[] = [\n \"newArchitecture\",\n \"newArchUnknown\",\n \"lastPublish\",\n \"githubArchived\",\n \"npmDeprecated\",\n \"directoryUnmaintained\",\n];\nconst TOP_LEVEL_KEYS = [\"rules\", \"scope\", \"allow\"];\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isList(value: unknown): value is readonly unknown[] {\n return Array.isArray(value);\n}\n\nfunction fail(where: string, problem: string): never {\n throw new PolicyError(`Invalid policy${where}: ${problem}`);\n}\n\nfunction parseSeverity(value: unknown, key: string, where: string): RuleSeverity {\n if (typeof value === \"string\" && (RULE_SEVERITIES as readonly string[]).includes(value)) {\n return value as RuleSeverity;\n }\n return fail(where, `rules.${key} must be one of ${RULE_SEVERITIES.join(\" | \")}, got ${JSON.stringify(value)}`);\n}\n\nfunction parseLastPublish(value: unknown, where: string): PolicyRules[\"lastPublish\"] {\n if (value === \"off\") return \"off\";\n if (isRecord(value)) {\n const keys = Object.keys(value);\n const unknown = keys.filter((k) => k !== \"warnMonths\" && k !== \"errorMonths\");\n if (unknown.length > 0) {\n fail(where, `rules.lastPublish has unknown key(s): ${unknown.join(\", \")} (expected warnMonths, errorMonths)`);\n }\n const { warnMonths, errorMonths } = value;\n if (typeof warnMonths !== \"number\" || typeof errorMonths !== \"number\") {\n fail(where, `rules.lastPublish.warnMonths and .errorMonths must both be numbers`);\n }\n if (warnMonths < 0 || errorMonths < 0 || errorMonths < warnMonths) {\n fail(where, `rules.lastPublish thresholds must be >= 0 with errorMonths >= warnMonths`);\n }\n return { warnMonths, errorMonths };\n }\n return fail(where, `rules.lastPublish must be \"off\" or { warnMonths, errorMonths }, got ${JSON.stringify(value)}`);\n}\n\nfunction parseRules(value: unknown, where: string): PolicyRules {\n if (!isRecord(value)) {\n return fail(where, `\"rules\" must be a mapping, got ${JSON.stringify(value)}`);\n }\n const unknown = Object.keys(value).filter((k) => !(RULE_KEYS as readonly string[]).includes(k));\n if (unknown.length > 0) {\n fail(where, `unknown rule(s): ${unknown.join(\", \")} (known rules: ${RULE_KEYS.join(\", \")})`);\n }\n const defaults = DEFAULT_POLICY.rules;\n const severity = (key: keyof PolicyRules, fallback: RuleSeverity): RuleSeverity =>\n key in value ? parseSeverity(value[key], key, where) : fallback;\n return {\n newArchitecture: severity(\"newArchitecture\", defaults.newArchitecture),\n newArchUnknown: severity(\"newArchUnknown\", defaults.newArchUnknown),\n lastPublish: \"lastPublish\" in value ? parseLastPublish(value.lastPublish, where) : defaults.lastPublish,\n githubArchived: severity(\"githubArchived\", defaults.githubArchived),\n npmDeprecated: severity(\"npmDeprecated\", defaults.npmDeprecated),\n directoryUnmaintained: severity(\"directoryUnmaintained\", defaults.directoryUnmaintained),\n };\n}\n\nconst EXPIRES_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nfunction parseAllowEntry(value: unknown, index: number, where: string): AllowEntry {\n const at = `allow[${String(index)}]`;\n if (!isRecord(value)) {\n return fail(where, `${at} must be a mapping with a \"package\" key, got ${JSON.stringify(value)}`);\n }\n const unknown = Object.keys(value).filter((k) => k !== \"package\" && k !== \"reason\" && k !== \"expires\");\n if (unknown.length > 0) {\n fail(where, `${at} has unknown key(s): ${unknown.join(\", \")} (expected package, reason, expires)`);\n }\n const pkg = value.package;\n if (typeof pkg !== \"string\" || pkg.length === 0) {\n fail(where, `${at}.package must be a non-empty string`);\n }\n const reason = value.reason;\n if (reason !== undefined && typeof reason !== \"string\") {\n fail(where, `${at}.reason must be a string when present`);\n }\n let expires = value.expires;\n // The yaml parser turns unquoted dates into Date objects; normalize back.\n if (expires instanceof Date && !Number.isNaN(expires.getTime())) {\n expires = expires.toISOString().slice(0, 10);\n }\n if (expires !== undefined && (typeof expires !== \"string\" || !EXPIRES_PATTERN.test(expires))) {\n fail(where, `${at}.expires must be a YYYY-MM-DD date when present, got ${JSON.stringify(value.expires)}`);\n }\n return {\n package: pkg,\n reason: typeof reason === \"string\" ? reason : null,\n expires: typeof expires === \"string\" ? expires : null,\n };\n}\n\n/**\n * Parse and validate policy YAML text into a complete {@link Policy}.\n *\n * Absent keys fall back to {@link DEFAULT_POLICY}; unknown keys and invalid\n * values throw {@link PolicyError} with the offending key named.\n *\n * @param yamlText - Raw contents of a `.rn-doctor.yml` file.\n * @param filePath - Optional path used to contextualize error messages.\n * @returns The validated policy, merged over the defaults.\n */\nexport function parsePolicy(yamlText: string, filePath?: string): Policy {\n const where = filePath ? ` (${filePath})` : \"\";\n\n let raw: unknown;\n try {\n raw = parse(yamlText);\n } catch (err) {\n return fail(where, `YAML syntax error — ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (raw === null || raw === undefined) return DEFAULT_POLICY;\n if (!isRecord(raw)) {\n return fail(where, `top level must be a mapping, got ${JSON.stringify(raw)}`);\n }\n\n const unknown = Object.keys(raw).filter((k) => !TOP_LEVEL_KEYS.includes(k));\n if (unknown.length > 0) {\n fail(where, `unknown top-level key(s): ${unknown.join(\", \")} (expected ${TOP_LEVEL_KEYS.join(\", \")})`);\n }\n\n const rules = \"rules\" in raw ? parseRules(raw.rules, where) : DEFAULT_POLICY.rules;\n\n let scope: PolicyScope = DEFAULT_POLICY.scope;\n if (\"scope\" in raw) {\n const value = raw.scope;\n if (typeof value !== \"string\" || !(SCOPES as readonly string[]).includes(value)) {\n fail(where, `\"scope\" must be one of ${SCOPES.join(\" | \")}, got ${JSON.stringify(value)}`);\n }\n scope = value as PolicyScope;\n }\n\n let allow: readonly AllowEntry[] = DEFAULT_POLICY.allow;\n if (\"allow\" in raw) {\n const value = raw.allow;\n if (!isList(value)) {\n return fail(where, `\"allow\" must be a list of entries, got ${JSON.stringify(value)}`);\n }\n allow = value.map((entry, i) => parseAllowEntry(entry, i, where));\n }\n\n return { rules, scope, allow };\n}\n\n/**\n * Load the effective policy from disk.\n *\n * @remarks\n * - With an explicit `path`: the file must exist — a missing file is a\n * {@link PolicyError} (a CI run pointing at a typo'd path must not\n * silently fall back to defaults).\n * - Without a `path`: looks for `.rn-doctor.yml` in `cwd`; if absent,\n * returns {@link DEFAULT_POLICY}.\n *\n * @param path - Optional explicit policy file path.\n * @param cwd - Directory searched for the default file. Defaults to `process.cwd()`.\n * @returns The validated effective policy.\n */\nexport async function loadPolicy(path?: string, cwd: string = process.cwd()): Promise<Policy> {\n const explicit = path !== undefined;\n const filePath = explicit ? resolve(cwd, path) : resolve(cwd, DEFAULT_POLICY_FILENAME);\n\n let text: string;\n try {\n text = await readFile(filePath, \"utf8\");\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\" && !explicit) return DEFAULT_POLICY;\n throw new PolicyError(\n explicit && code === \"ENOENT\"\n ? `Policy file not found: ${filePath}. Check the --policy path.`\n : `Could not read policy file ${filePath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n return parsePolicy(text, filePath);\n}\n","/**\n * Shared reporter surface: the report shape every renderer consumes, the\n * finding summary, and the exit-code contract.\n * @packageDocumentation\n */\n\nimport type { Finding } from \"./policy.js\";\nimport type { EnrichmentWarning } from \"./types.js\";\n\n/**\n * A policy finding located in a specific manifest file. The CLI decorates\n * pure policy findings with their manifest path at report-assembly time; the\n * policy engine itself stays manifest-blind.\n */\nexport interface ReportFinding extends Finding {\n /**\n * Manifest path relative to the run cwd, POSIX separators — `package.json`\n * for single-manifest runs, e.g. `packages/a/package.json` under\n * `--workspaces`.\n */\n readonly file: string;\n}\n\n/**\n * Everything a reporter needs to render one rn-doctor run.\n */\nexport interface Report {\n /** Findings from the policy engine, grouped by manifest in its stable output order. */\n readonly findings: readonly ReportFinding[];\n /** Run-level and per-dependency enrichment warnings (degraded data, etc.). */\n readonly warnings: readonly EnrichmentWarning[];\n /** How many (manifest, dependency) pairs were checked across all scanned manifests. */\n readonly checkedCount: number;\n /**\n * How many manifests were scanned. Omitted (equivalent to 1) outside\n * `--workspaces`; the pretty reporter mentions it only when above 1.\n */\n readonly manifestCount?: number;\n}\n\n/**\n * Decorate pure policy findings with the manifest they belong to.\n *\n * @param findings - Findings from {@link evaluatePolicy}.\n * @param file - Manifest path relative to the run cwd, POSIX separators.\n */\nexport function locateFindings(\n findings: readonly Finding[],\n file: string,\n): readonly ReportFinding[] {\n return findings.map((f) => ({ ...f, file }));\n}\n\n/**\n * Counts of findings by effect. Suppressed findings are counted only under\n * `suppressed` — they keep their severity for display but have no effect on\n * the run outcome.\n */\nexport interface FindingSummary {\n readonly errors: number;\n readonly warnings: number;\n readonly notes: number;\n readonly suppressed: number;\n}\n\n/**\n * Summarize findings for the pretty footer and the JSON document.\n */\nexport function summarize(findings: readonly Finding[]): FindingSummary {\n let errors = 0;\n let warnings = 0;\n let notes = 0;\n let suppressed = 0;\n for (const f of findings) {\n if (f.suppressedBy !== null) {\n suppressed++;\n } else if (f.severity === \"error\") {\n errors++;\n } else if (f.severity === \"warn\") {\n warnings++;\n } else {\n notes++;\n }\n }\n return { errors, warnings, notes, suppressed };\n}\n\n/**\n * The stable exit-code contract for policy outcomes.\n *\n * @remarks\n * Returns `1` iff any finding is an unsuppressed `error`; warnings, notes,\n * and allowlist-suppressed findings never fail the run. Exit code `2`\n * (tool failure) is decided by the CLI, not here — a report that rendered\n * at all is not a tool failure.\n */\nexport function computeExitCode(findings: readonly Finding[]): 0 | 1 {\n return findings.some((f) => f.severity === \"error\" && f.suppressedBy === null) ? 1 : 0;\n}\n","/**\n * JSON reporter: a machine-readable document with a stable key order, safe\n * to snapshot and to diff across runs.\n * @packageDocumentation\n */\n\nimport { summarize } from \"./report.js\";\nimport type { Report } from \"./report.js\";\n\n/**\n * Render the report as a stable-ordered JSON document.\n *\n * @remarks\n * Key order is fixed by construction (every object is rebuilt field by\n * field), and the document contains no timestamps or environment-dependent\n * values, so identical inputs always serialize identically. The `version`\n * field is the document format version, bumped only on breaking shape\n * changes — additive fields (like `file`, added for `--workspaces`) do not\n * bump it.\n *\n * @param report - The report to render.\n * @returns Pretty-printed JSON, terminated with a newline.\n */\nexport function renderJson(report: Report): string {\n const summary = summarize(report.findings);\n\n const doc = {\n version: 1,\n summary: {\n checked: report.checkedCount,\n errors: summary.errors,\n warnings: summary.warnings,\n notes: summary.notes,\n suppressed: summary.suppressed,\n },\n findings: report.findings.map((f) => ({\n file: f.file,\n package: f.package,\n rule: f.rule,\n severity: f.severity,\n message: f.message,\n evidenceUrl: f.evidenceUrl,\n suppressedBy: f.suppressedBy === null\n ? null\n : { reason: f.suppressedBy.reason, expires: f.suppressedBy.expires },\n })),\n warnings: report.warnings.map((w) => ({\n ...(w.dependency !== undefined ? { dependency: w.dependency } : {}),\n source: w.source,\n message: w.message,\n })),\n };\n\n return `${JSON.stringify(doc, null, 2)}\\n`;\n}\n","/**\n * SARIF 2.1.0 reporter, for GitHub code scanning and other SARIF consumers.\n * The output validates against the OASIS SARIF 2.1.0 schema (enforced by the\n * acceptance suite).\n * @packageDocumentation\n */\n\nimport type { Finding, RuleId } from \"./policy.js\";\nimport { summarize } from \"./report.js\";\nimport type { Report } from \"./report.js\";\nimport { VERSION } from \"./version.js\";\n\nconst INFORMATION_URI = \"https://www.npmjs.com/package/react-native-doctor-ci\";\n\n/** Fixed rule order — also the `ruleIndex` mapping for results. */\nconst RULE_IDS: readonly RuleId[] = [\n \"newArchitecture\",\n \"newArchUnknown\",\n \"lastPublish\",\n \"githubArchived\",\n \"npmDeprecated\",\n \"directoryUnmaintained\",\n];\n\nconst RULE_DESCRIPTIONS: Readonly<Record<RuleId, string>> = {\n newArchitecture:\n \"The package does not support the React Native New Architecture, per the React Native Directory.\",\n newArchUnknown:\n \"New Architecture support for the package could not be determined from the React Native Directory or npm codegen hints.\",\n lastPublish: \"The package's latest npm publish is older than the configured staleness threshold.\",\n githubArchived: \"The package's GitHub repository is archived (read-only).\",\n npmDeprecated: \"The package is marked deprecated on the npm registry.\",\n directoryUnmaintained: \"The React Native Directory flags the package as unmaintained.\",\n};\n\n/** Map finding severity to a SARIF result level. */\nfunction sarifLevel(severity: Finding[\"severity\"]): \"error\" | \"warning\" | \"note\" {\n return severity === \"warn\" ? \"warning\" : severity;\n}\n\n/**\n * Options for {@link renderSarif}.\n */\nexport interface SarifOptions {\n /**\n * Resolve a package's 1-based declaration line in the manifest at `file`\n * (the finding's cwd-relative manifest path), or `null` when unknown. When\n * omitted (or when it returns `null`), results still carry the manifest\n * artifact location, just without a region.\n */\n readonly lineOf?: (file: string, packageName: string) => number | null;\n}\n\n/**\n * Render the report as a SARIF 2.1.0 log with a single run.\n *\n * @remarks\n * Severity maps `error` → `\"error\"`, `warn` → `\"warning\"`, `note` → `\"note\"`.\n * Allowlist-suppressed findings are emitted with a `suppressions` entry\n * (`kind: \"external\"`, `status: \"accepted\"`) so SARIF consumers exclude them\n * from gating, mirroring the CLI exit-code contract. Enrichment warnings are\n * carried as tool execution notifications on the invocation.\n *\n * @param report - The report to render.\n * @param options - Line resolution for annotating package.json regions.\n * @returns Pretty-printed SARIF JSON, terminated with a newline.\n */\nexport function renderSarif(report: Report, options: SarifOptions = {}): string {\n const lineOf = options.lineOf ?? (() => null);\n const summary = summarize(report.findings);\n\n const results = report.findings.map((f) => {\n const line = lineOf(f.file, f.package);\n return {\n ruleId: f.rule,\n ruleIndex: RULE_IDS.indexOf(f.rule),\n level: sarifLevel(f.severity),\n message: { text: f.message },\n locations: [\n {\n physicalLocation: {\n artifactLocation: { uri: f.file },\n ...(line !== null ? { region: { startLine: line } } : {}),\n },\n },\n ],\n ...(f.suppressedBy !== null\n ? {\n suppressions: [\n {\n kind: \"external\" as const,\n status: \"accepted\" as const,\n justification:\n `Allowlisted in .rn-doctor.yml` +\n (f.suppressedBy.reason ? `: ${f.suppressedBy.reason}` : \"\") +\n (f.suppressedBy.expires ? ` (expires ${f.suppressedBy.expires})` : \"\"),\n },\n ],\n }\n : {}),\n ...(f.evidenceUrl !== null ? { hostedViewerUri: f.evidenceUrl } : {}),\n };\n });\n\n const doc = {\n $schema: \"https://json.schemastore.org/sarif-2.1.0.json\",\n version: \"2.1.0\" as const,\n runs: [\n {\n tool: {\n driver: {\n name: \"rn-doctor\",\n informationUri: INFORMATION_URI,\n version: VERSION,\n rules: RULE_IDS.map((id) => ({\n id,\n shortDescription: { text: RULE_DESCRIPTIONS[id] },\n helpUri: INFORMATION_URI,\n })),\n },\n },\n invocations: [\n {\n executionSuccessful: true,\n toolExecutionNotifications: report.warnings.map((w) => ({\n level: \"warning\" as const,\n message: {\n text: (w.dependency !== undefined ? `${w.dependency}: ` : \"\") + w.message,\n },\n })),\n },\n ],\n results,\n properties: {\n checked: report.checkedCount,\n errors: summary.errors,\n warnings: summary.warnings,\n notes: summary.notes,\n suppressed: summary.suppressed,\n },\n },\n ],\n };\n\n return `${JSON.stringify(doc, null, 2)}\\n`;\n}\n","/**\n * Human-readable terminal reporter: one block per finding (verdict, reason,\n * evidence link), enrichment warnings, and a summary footer.\n * @packageDocumentation\n */\n\nimport type { Finding } from \"./policy.js\";\nimport { summarize } from \"./report.js\";\nimport type { Report } from \"./report.js\";\n\n/**\n * Options for {@link renderPretty}.\n */\nexport interface PrettyOptions {\n /**\n * Enable ANSI colors. The CLI passes `stdout.isTTY && !NO_COLOR`; tests\n * pass `false` for stable snapshots.\n */\n readonly color: boolean;\n}\n\nconst ESC = String.fromCharCode(27);\n\nconst ANSI = {\n red: \"31\",\n yellow: \"33\",\n cyan: \"36\",\n green: \"32\",\n dim: \"2\",\n bold: \"1\",\n} as const;\n\ntype AnsiCode = (typeof ANSI)[keyof typeof ANSI];\n\nfunction paint(code: AnsiCode, text: string, on: boolean): string {\n return on ? `${ESC}[${code}m${text}${ESC}[0m` : text;\n}\n\n/** The display badge for a finding, after suppression is taken into account. */\nfunction badge(f: Finding, color: boolean): string {\n if (f.suppressedBy !== null) return paint(ANSI.dim, `allowed(${f.severity})`, color);\n if (f.severity === \"error\") return paint(ANSI.red, \"error\", color);\n if (f.severity === \"warn\") return paint(ANSI.yellow, \"warn\", color);\n return paint(ANSI.cyan, \"note\", color);\n}\n\nfunction plural(n: number, word: string): string {\n return `${String(n)} ${word}${n === 1 ? \"\" : \"s\"}`;\n}\n\n/**\n * Render the report for human eyes.\n *\n * @remarks\n * When the findings span more than one manifest (a `--workspaces` run), each\n * group is introduced by its manifest path; single-manifest output is\n * unchanged.\n *\n * @param report - The report to render.\n * @param options - Color toggling; content is identical either way.\n * @returns The full report text, terminated with a newline.\n */\nexport function renderPretty(report: Report, options: PrettyOptions): string {\n const { color } = options;\n const lines: string[] = [];\n\n const multiManifest = (report.manifestCount ?? 1) > 1;\n let currentFile: string | null = null;\n\n for (const f of report.findings) {\n if (multiManifest && f.file !== currentFile) {\n currentFile = f.file;\n lines.push(paint(ANSI.dim, `${f.file}:`, color));\n lines.push(\"\");\n }\n lines.push(`${badge(f, color)} ${paint(ANSI.bold, f.package, color)} [${f.rule}]`);\n lines.push(` ${f.message}`);\n if (f.suppressedBy !== null) {\n const reason = f.suppressedBy.reason ?? \"no reason given\";\n const expires = f.suppressedBy.expires ? `, expires ${f.suppressedBy.expires}` : \"\";\n lines.push(paint(ANSI.dim, ` allowed by .rn-doctor.yml: ${reason}${expires}`, color));\n }\n if (f.evidenceUrl !== null) {\n lines.push(paint(ANSI.dim, ` evidence: ${f.evidenceUrl}`, color));\n }\n lines.push(\"\");\n }\n\n if (report.warnings.length > 0) {\n lines.push(\"Data warnings (missing or degraded lookups - findings may be incomplete):\");\n for (const w of report.warnings) {\n const scope = w.dependency !== undefined ? `${w.dependency}: ` : \"\";\n lines.push(paint(ANSI.dim, ` - [${w.source}] ${scope}${w.message}`, color));\n }\n lines.push(\"\");\n }\n\n const s = summarize(report.findings);\n const n = report.checkedCount;\n const checked =\n `${String(n)} ${n === 1 ? \"dependency\" : \"dependencies\"}` +\n (multiManifest ? ` in ${String(report.manifestCount)} manifests` : \"\");\n if (s.errors === 0 && s.warnings === 0 && s.notes === 0 && s.suppressed === 0) {\n lines.push(paint(ANSI.green, `No findings across ${checked}.`, color));\n } else {\n const parts = [\n paint(ANSI.red, plural(s.errors, \"error\"), color),\n paint(ANSI.yellow, plural(s.warnings, \"warning\"), color),\n plural(s.notes, \"note\"),\n `${String(s.suppressed)} allowed`,\n ];\n lines.push(`${parts.join(\", \")} across ${checked}.`);\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n","/**\n * GitHub Actions annotation reporter: emits workflow commands\n * (`::error file=package.json,line=N::message`) so findings appear inline on\n * the PR diff.\n * @packageDocumentation\n */\n\nimport type { Finding } from \"./policy.js\";\nimport type { Report } from \"./report.js\";\n\n/**\n * Escape a workflow-command message (the part after `::`).\n * Per the GitHub Actions runner: `%` must be escaped first.\n */\nfunction escapeData(value: string): string {\n return value.replaceAll(\"%\", \"%25\").replaceAll(\"\\r\", \"%0D\").replaceAll(\"\\n\", \"%0A\");\n}\n\n/**\n * Escape a workflow-command property value (e.g. `file=`, `title=`).\n * Property values additionally escape `,` and `:`, which delimit properties.\n */\nfunction escapeProperty(value: string): string {\n return escapeData(value).replaceAll(\":\", \"%3A\").replaceAll(\",\", \"%2C\");\n}\n\n/**\n * The annotation command for a finding. Suppressed findings and notes are\n * informational: they surface as `notice`, never as a failing `error`.\n */\nfunction commandFor(f: Finding): \"error\" | \"warning\" | \"notice\" {\n if (f.suppressedBy !== null || f.severity === \"note\") return \"notice\";\n return f.severity === \"error\" ? \"error\" : \"warning\";\n}\n\n/**\n * Render the report as GitHub Actions workflow commands, one per finding.\n *\n * @remarks\n * Each command targets the finding's manifest (`package.json`, or e.g.\n * `packages/a/package.json` under `--workspaces`) with the dependency's\n * declaration line when `lineOf` resolves one (omitting `line=` otherwise),\n * so annotations land inline on the PR diff. Suppressed findings keep an\n * annotation (as a `notice` including the allow reason) so policy debt stays\n * visible without failing checks.\n *\n * @param report - The report to render.\n * @param lineOf - Resolve a package's 1-based declaration line in the\n * manifest at `file` (the finding's cwd-relative path), or `null`.\n * @returns Newline-terminated workflow commands; empty string when there are\n * no findings.\n */\nexport function renderAnnotations(\n report: Report,\n lineOf: (file: string, packageName: string) => number | null,\n): string {\n const lines: string[] = [];\n\n for (const f of report.findings) {\n const command = commandFor(f);\n const line = lineOf(f.file, f.package);\n const properties = [\n `file=${escapeProperty(f.file)}`,\n ...(line !== null ? [`line=${String(line)}`] : []),\n `title=${escapeProperty(`rn-doctor: ${f.rule} (${f.package})`)}`,\n ].join(\",\");\n\n const suffix =\n f.suppressedBy !== null\n ? ` [allowed${f.suppressedBy.reason ? `: ${f.suppressedBy.reason}` : \"\"}` +\n `${f.suppressedBy.expires ? `, expires ${f.suppressedBy.expires}` : \"\"}]`\n : \"\";\n\n lines.push(`::${command} ${properties}::${escapeData(f.message + suffix)}`);\n }\n\n return lines.length > 0 ? `${lines.join(\"\\n\")}\\n` : \"\";\n}\n","/**\n * package.json reading and dependency-line resolution for the CLI and the\n * GitHub-annotation reporter.\n *\n * The enrichment engine itself stays manifest-agnostic (it takes plain\n * package names); everything in this module is CLI-side plumbing.\n *\n * @packageDocumentation\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\n/**\n * A project manifest could not be read or parsed. Maps to exit code 2\n * (tool failure) in the CLI.\n */\nexport class ManifestError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ManifestError\";\n }\n}\n\n/**\n * One dependency declaration from a `dependencies` section: the package name\n * and its raw version spec string, exactly as authored.\n */\nexport interface DependencyEntry {\n readonly name: string;\n /** The raw spec (`\"^1.2.0\"`, `\"npm:foo@2\"`, …) — never parsed as semver. */\n readonly spec: string;\n}\n\n/**\n * A loaded package.json: the raw text (for line resolution) and the\n * dependency names to check, in the order they are authored.\n */\nexport interface ProjectManifest {\n /** Absolute path the manifest was read from. */\n readonly path: string;\n /** The raw file text, used to resolve dependency line numbers. */\n readonly text: string;\n /** Names from the `dependencies` section, in authored order. */\n readonly dependencies: readonly string[];\n /** Name/spec pairs from the `dependencies` section, in authored order. */\n readonly entries: readonly DependencyEntry[];\n}\n\n/**\n * Extract name/spec pairs from the `dependencies` section of a parsed\n * package.json value, preserving authored order. Returns an empty list when\n * the section is absent; throws {@link ManifestError} when the section or a\n * spec value has the wrong shape.\n *\n * @param parsed - The JSON-parsed manifest.\n * @param where - Human-readable location used in error messages.\n */\nexport function listDependencyEntries(\n parsed: unknown,\n where = \"package.json\",\n): readonly DependencyEntry[] {\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n throw new ManifestError(`${where} is not a JSON object.`);\n }\n const deps: unknown = (parsed as Record<string, unknown>)[\"dependencies\"];\n if (deps === undefined) return [];\n if (typeof deps !== \"object\" || deps === null || Array.isArray(deps)) {\n throw new ManifestError(`${where} has a \"dependencies\" field that is not an object.`);\n }\n return Object.entries(deps).map(([name, spec]) => {\n if (typeof spec !== \"string\") {\n throw new ManifestError(`${where} has a non-string version for dependency \"${name}\".`);\n }\n return { name, spec };\n });\n}\n\n/**\n * Extract the names of the `dependencies` section from a parsed package.json\n * value, preserving authored order. Returns an empty list when the section is\n * absent; throws {@link ManifestError} when it is present but not an object.\n *\n * @param parsed - The JSON-parsed manifest.\n * @param where - Human-readable location used in error messages.\n */\nexport function listDependencies(parsed: unknown, where = \"package.json\"): readonly string[] {\n return listDependencyEntries(parsed, where).map((entry) => entry.name);\n}\n\n/**\n * Parse raw manifest text (e.g. a blob read from git) into dependency\n * entries. BOM-tolerant like {@link readPackageJson}.\n *\n * @param text - The raw manifest text.\n * @param where - Human-readable location used in error messages.\n * @throws ManifestError when the text is not valid JSON or has a malformed\n * `dependencies` section.\n */\nexport function entriesFromManifestText(text: string, where: string): readonly DependencyEntry[] {\n const stripped = text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;\n let parsed: unknown;\n try {\n parsed = JSON.parse(stripped) as unknown;\n } catch (err) {\n throw new ManifestError(\n `${where} is not valid JSON — ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n return listDependencyEntries(parsed, where);\n}\n\n/**\n * Read and parse the manifest at an exact path.\n *\n * @param path - Absolute path of the package.json to read.\n * @param missingMessage - Error message when the file does not exist; defaults\n * to a plain \"not found\" pointing at `path`.\n * @returns The manifest text and its dependency entries.\n * @throws ManifestError when the file is missing or not valid JSON — the CLI\n * turns this into exit code 2 with the message shown as-is.\n */\nexport async function readManifestAt(\n path: string,\n missingMessage?: string,\n): Promise<ProjectManifest> {\n let text: string;\n try {\n text = await readFile(path, \"utf8\");\n // npm tolerates a UTF-8 BOM in package.json; JSON.parse does not. The BOM\n // sits before line 1's content, so stripping it never shifts line numbers.\n if (text.charCodeAt(0) === 0xfeff) text = text.slice(1);\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\") {\n throw new ManifestError(missingMessage ?? `No package.json found at ${path}.`);\n }\n throw new ManifestError(\n `Could not read ${path} — ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(text) as unknown;\n } catch (err) {\n throw new ManifestError(\n `${path} is not valid JSON — ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n const entries = listDependencyEntries(parsed, path);\n return { path, text, dependencies: entries.map((entry) => entry.name), entries };\n}\n\n/**\n * Read and parse `<cwd>/package.json`.\n *\n * @param cwd - Directory containing the manifest.\n * @returns The manifest text and its dependency names.\n * @throws ManifestError when the file is missing or not valid JSON — the CLI\n * turns this into exit code 2 with the message shown as-is.\n */\nexport async function readPackageJson(cwd: string): Promise<ProjectManifest> {\n return readManifestAt(\n join(cwd, \"package.json\"),\n `No package.json found in ${cwd}. Run rn-doctor from the project root ` +\n `(the directory containing package.json).`,\n );\n}\n\n/**\n * Resolve the 1-based line number where `name` is declared inside the\n * top-level `dependencies` object of a package.json text.\n *\n * @remarks\n * Scans the raw text with a minimal JSON walker (string- and escape-aware,\n * tracking object depth), so a key with the same name under `devDependencies`,\n * `scripts`, or a nested object can never false-match. Returns `null` when the\n * name is not declared in `dependencies` — annotations then omit the line.\n *\n * @param text - The raw package.json text (not re-serialized — the real file).\n * @param name - The exact dependency name to locate.\n */\nexport function findDependencyLine(text: string, name: string): number | null {\n let line = 1;\n let depth = 0;\n let inDependencies = false;\n\n let i = 0;\n while (i < text.length) {\n const ch = text[i];\n\n if (ch === \"\\n\") {\n line++;\n i++;\n continue;\n }\n\n if (ch === '\"') {\n // Consume the whole string, tracking lines and the literal content.\n const stringLine = line;\n let value = \"\";\n i++;\n while (i < text.length) {\n const c = text[i];\n if (c === \"\\\\\") {\n value += text.slice(i, i + 2);\n i += 2;\n continue;\n }\n if (c === \"\\n\") line++; // Invalid in JSON, but keep the count honest.\n if (c === '\"') {\n i++;\n break;\n }\n value += c;\n i++;\n }\n // A string followed by \":\" (after whitespace) is an object key.\n let j = i;\n while (j < text.length && (text[j] === \" \" || text[j] === \"\\t\" || text[j] === \"\\r\" || text[j] === \"\\n\")) j++;\n const isKey = text[j] === \":\";\n if (isKey) {\n if (depth === 1 && value === \"dependencies\") {\n // Only an object-valued `dependencies` opens the section; a scalar\n // value must not leak the flag onto sibling objects.\n let k = j + 1;\n while (k < text.length && (text[k] === \" \" || text[k] === \"\\t\" || text[k] === \"\\r\" || text[k] === \"\\n\")) k++;\n if (text[k] === \"{\") inDependencies = true;\n } else if (inDependencies && depth === 2 && value === name) {\n return stringLine;\n }\n }\n continue;\n }\n\n if (ch === \"{\" || ch === \"[\") {\n depth++;\n } else if (ch === \"}\" || ch === \"]\") {\n depth--;\n if (inDependencies && depth <= 1) inDependencies = false;\n }\n i++;\n }\n\n return null;\n}\n","/**\n * Pure `--changed-only` semantics: which dependencies of the current manifest\n * were added or changed relative to the base manifest. No git knowledge here —\n * the CLI composes this with the blob reads from `git.ts`.\n *\n * @packageDocumentation\n */\n\nimport type { DependencyEntry } from \"./package-json.js\";\n\n/**\n * Names to check under `--changed-only`: additions (name absent at base) and\n * changes (raw spec string differs — upgrades, downgrades, and protocol\n * changes like an `npm:` alias all count; re-checking is always safe).\n * Removed and unchanged dependencies are skipped.\n *\n * @param base - Entries at the base commit, or `null` when the manifest did\n * not exist there — every current dependency then counts as added.\n * @param current - Entries in the working-tree manifest.\n * @returns Names in `current` authored order.\n */\nexport function diffDependencies(\n base: readonly DependencyEntry[] | null,\n current: readonly DependencyEntry[],\n): readonly string[] {\n if (base === null) return current.map((entry) => entry.name);\n\n const baseSpecs = new Map(base.map((entry) => [entry.name, entry.spec]));\n return current\n .filter((entry) => baseSpecs.get(entry.name) !== entry.spec)\n .map((entry) => entry.name);\n}\n","/**\n * Git plumbing for `--changed-only`: resolve the diff base (merge-base of HEAD\n * and the base ref) and read manifest blobs at that commit.\n *\n * This is the only module in the codebase that touches `child_process`; the\n * process runner is injectable so everything above it is testable without a\n * real repository.\n *\n * @packageDocumentation\n */\n\nimport { execFile } from \"node:child_process\";\n\n/**\n * A git invocation failed in a way rn-doctor cannot recover from. Maps to\n * exit code 2 (tool failure) in the CLI; the message says what to do.\n */\nexport class GitError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"GitError\";\n }\n}\n\n/** Result of one git invocation. */\nexport interface GitRunResult {\n /** Process exit code (0 = success). */\n readonly code: number;\n readonly stdout: string;\n readonly stderr: string;\n}\n\n/**\n * Injectable git process runner. Resolves with the exit code rather than\n * rejecting on nonzero exit — callers classify failures themselves. Throws\n * {@link GitError} only when git cannot be spawned at all.\n */\nexport type GitRunner = (args: readonly string[], cwd: string) => Promise<GitRunResult>;\n\n/**\n * The real runner: spawns the `git` binary via `execFile` (args array, no\n * shell, so refs and Windows paths need no quoting).\n */\nexport function createGitRunner(): GitRunner {\n return (args, cwd) =>\n new Promise((resolve, reject) => {\n execFile(\n \"git\",\n [...args],\n { cwd, encoding: \"utf8\", windowsHide: true, maxBuffer: 16 * 1024 * 1024 },\n (err, stdout, stderr) => {\n if (err && (err as NodeJS.ErrnoException).code === \"ENOENT\") {\n reject(\n new GitError(\"git executable not found on PATH — --changed-only requires git.\"),\n );\n return;\n }\n // execFile sets err for nonzero exit too; surface the code instead.\n const code = err ? ((err as { code?: number | string }).code ?? 1) : 0;\n resolve({ code: typeof code === \"number\" ? code : 1, stdout, stderr });\n },\n );\n });\n}\n\n/**\n * Resolve the commit to diff against: `merge-base(HEAD, baseRef)`.\n *\n * @remarks\n * The merge-base (not the base tip) reproduces GitHub's three-dot PR diff:\n * dependency changes that landed on the base branch after the PR diverged are\n * never attributed to the PR. On the base branch itself the merge-base is HEAD,\n * so `--changed-only` correctly reports zero changes.\n *\n * @param git - The process runner.\n * @param cwd - Directory to run git in (the project root being checked).\n * @param baseRef - The base ref, e.g. `origin/main`.\n * @returns The merge-base commit SHA.\n * @throws GitError with an actionable message when the cwd is not a repo, the\n * ref does not exist, or the histories share no common ancestor.\n */\nexport async function resolveBaseCommit(\n git: GitRunner,\n cwd: string,\n baseRef: string,\n): Promise<string> {\n const result = await git([\"merge-base\", \"HEAD\", baseRef], cwd);\n if (result.code === 0) {\n const sha = result.stdout.trim();\n if (sha !== \"\") return sha;\n }\n\n const stderr = result.stderr;\n if (/not a git repository/i.test(stderr)) {\n throw new GitError(\n \"--changed-only requires running inside a git repository \" +\n \"(or drop the flag to check all dependencies).\",\n );\n }\n if (/not a valid object name|unknown revision|bad revision|needed a single revision/i.test(stderr)) {\n throw new GitError(\n `base ref \"${baseRef}\" was not found. Fetch it first (git fetch origin) or pass ` +\n `--base <ref>. In GitHub Actions, check out with fetch-depth: 0.`,\n );\n }\n if (stderr.trim() === \"\") {\n // merge-base exits 1 with empty output when there is no common ancestor.\n throw new GitError(\n `no merge base between HEAD and \"${baseRef}\" — histories are unrelated or the ` +\n `clone is too shallow. In GitHub Actions, check out with fetch-depth: 0.`,\n );\n }\n throw new GitError(`git merge-base HEAD ${baseRef} failed — ${stderr.trim()}`);\n}\n\n/**\n * Read the contents of a file as it existed at `commit`, or `null` when the\n * path did not exist at that commit (e.g. a manifest created since the base).\n *\n * @remarks\n * Uses the `git show <commit>:./<path>` form: the `./` prefix makes git\n * resolve the path relative to `cwd`, so no repository-root discovery is\n * needed even when the project sits in a subdirectory of the repo. A UTF-8\n * BOM is stripped, mirroring {@link readPackageJson}.\n *\n * @param git - The process runner.\n * @param cwd - Directory to run git in.\n * @param commit - The commit SHA (from {@link resolveBaseCommit}).\n * @param relPath - Path relative to `cwd`; separators are normalized to `/`.\n * @throws GitError for any failure other than \"path absent at that commit\".\n */\nexport async function readFileAtCommit(\n git: GitRunner,\n cwd: string,\n commit: string,\n relPath: string,\n): Promise<string | null> {\n const posixPath = relPath.replaceAll(\"\\\\\", \"/\");\n const result = await git([\"show\", `${commit}:./${posixPath}`], cwd);\n\n if (result.code === 0) {\n let text = result.stdout;\n if (text.charCodeAt(0) === 0xfeff) text = text.slice(1);\n return text;\n }\n\n if (/does not exist in|exists on disk, but not in|but not in the working tree|path .* does not exist/i.test(result.stderr)) {\n return null;\n }\n throw new GitError(`git show ${commit}:./${posixPath} failed — ${result.stderr.trim()}`);\n}\n","/**\n * Workspace discovery for `--workspaces`: find every workspace package.json\n * declared by the root manifest, so the CLI can scan and report them as one\n * grouped run.\n *\n * Supports both workspace conventions:\n * - `pnpm-workspace.yaml` `packages:` globs (takes precedence — pnpm itself\n * ignores the package.json field), parsed with the existing `yaml` dep;\n * - root package.json `\"workspaces\"` (an array, or `{ packages: [...] }`).\n *\n * Glob expansion is hand-rolled on purpose (zero-dependency bias): literal\n * paths, `*` as a full or partial segment, `**`, and leading-`!` exclusions\n * cover what real-world workspace fields use.\n *\n * @packageDocumentation\n */\n\nimport { readdir, readFile, stat } from \"node:fs/promises\";\nimport { join, relative, sep } from \"node:path\";\n\nimport { parse } from \"yaml\";\n\n/**\n * `--workspaces` was requested but no workspace configuration exists or it is\n * malformed. Maps to exit code 2 (tool failure) in the CLI.\n */\nexport class WorkspaceError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"WorkspaceError\";\n }\n}\n\n/** One discovered manifest location. */\nexport interface WorkspaceDir {\n /** Absolute directory containing a package.json. */\n readonly dir: string;\n /**\n * Manifest path relative to the root cwd, POSIX separators — e.g.\n * `packages/a/package.json`. The root manifest is `package.json`.\n */\n readonly manifestRelPath: string;\n}\n\n/** Compile one pattern segment (`*` wildcards only) to a RegExp. */\nfunction segmentToRegExp(segment: string): RegExp {\n const escaped = segment.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\").replaceAll(\"*\", \"[^/\\\\\\\\]*\");\n return new RegExp(`^${escaped}$`);\n}\n\nasync function isDirectory(path: string): Promise<boolean> {\n try {\n return (await stat(path)).isDirectory();\n } catch {\n return false;\n }\n}\n\n/** List subdirectory names of `dir`, skipping node_modules and dot-dirs. */\nasync function listSubdirs(dir: string): Promise<readonly string[]> {\n let entries;\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n return [];\n }\n return entries\n .filter((e) => e.isDirectory() && e.name !== \"node_modules\" && !e.name.startsWith(\".\"))\n .map((e) => e.name);\n}\n\n/** All directories at any depth under `dir` (inclusive), same skip rules. */\nasync function walkDirs(dir: string): Promise<readonly string[]> {\n const out: string[] = [dir];\n for (const name of await listSubdirs(dir)) {\n out.push(...(await walkDirs(join(dir, name))));\n }\n return out;\n}\n\n/** Expand one inclusion pattern to absolute directories under `rootDir`. */\nasync function expandPattern(rootDir: string, pattern: string): Promise<readonly string[]> {\n // Normalize authored separators and trailing slashes.\n const segments = pattern.replaceAll(\"\\\\\", \"/\").replace(/\\/+$/, \"\").split(\"/\").filter((s) => s !== \"\" && s !== \".\");\n\n let dirs: readonly string[] = [rootDir];\n for (const [index, segment] of segments.entries()) {\n if (segment === \"**\") {\n // `**` matches zero or more directories; delegate the rest of the\n // pattern to every directory under the current set (inclusive).\n const rest = segments.slice(index + 1).join(\"/\");\n const under = (await Promise.all(dirs.map((d) => walkDirs(d)))).flat();\n if (rest === \"\") return under;\n return (await Promise.all(under.map((d) => expandPattern(d, rest)))).flat();\n }\n if (segment.includes(\"*\")) {\n const re = segmentToRegExp(segment);\n const next: string[] = [];\n for (const dir of dirs) {\n for (const name of await listSubdirs(dir)) {\n if (re.test(name)) next.push(join(dir, name));\n }\n }\n dirs = next;\n } else {\n const next: string[] = [];\n for (const dir of dirs) {\n const candidate = join(dir, segment);\n if (await isDirectory(candidate)) next.push(candidate);\n }\n dirs = next;\n }\n if (dirs.length === 0) return [];\n }\n return dirs;\n}\n\n/** POSIX-separated path of `dir` relative to `rootDir`. */\nfunction relPosix(rootDir: string, dir: string): string {\n return relative(rootDir, dir).split(sep).join(\"/\");\n}\n\n/**\n * Expand workspace glob patterns to absolute directories under `rootDir`,\n * deduplicated and sorted by relative path. Leading-`!` patterns exclude\n * directories matched by earlier inclusions (pnpm-workspace.yaml commonly\n * carries e.g. `!**\\/test/**`).\n *\n * @remarks Supported syntax: literal paths, `*` as a full or partial path\n * segment, and `**` (any depth). `node_modules` and dot-directories are never\n * traversed. Anything fancier (braces, extglobs) simply matches nothing —\n * the CLI warns when a configuration yields zero workspaces.\n */\nexport async function expandWorkspacePatterns(\n rootDir: string,\n patterns: readonly string[],\n): Promise<readonly string[]> {\n const included = new Map<string, string>(); // relPosix → absolute\n const exclusions: string[] = [];\n\n for (const pattern of patterns) {\n if (pattern.startsWith(\"!\")) {\n exclusions.push(pattern.slice(1));\n continue;\n }\n for (const dir of await expandPattern(rootDir, pattern)) {\n const rel = relPosix(rootDir, dir);\n if (rel !== \"\") included.set(rel, dir);\n }\n }\n\n for (const exclusion of exclusions) {\n const excluded = new Set(\n (await expandPattern(rootDir, exclusion)).map((dir) => relPosix(rootDir, dir)),\n );\n for (const rel of [...included.keys()]) {\n if (excluded.has(rel)) included.delete(rel);\n }\n }\n\n return [...included.entries()]\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .map(([, dir]) => dir);\n}\n\n/** Read the `packages:` globs from pnpm-workspace.yaml, or null when absent. */\nasync function readPnpmWorkspacePatterns(rootDir: string): Promise<readonly string[] | null> {\n let text: string;\n try {\n text = await readFile(join(rootDir, \"pnpm-workspace.yaml\"), \"utf8\");\n } catch {\n return null;\n }\n let parsed: unknown;\n try {\n parsed = parse(text) as unknown;\n } catch (err) {\n throw new WorkspaceError(\n `pnpm-workspace.yaml is not valid YAML — ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n if (typeof parsed !== \"object\" || parsed === null) return null;\n const packages: unknown = (parsed as Record<string, unknown>)[\"packages\"];\n if (packages === undefined) return null;\n if (!Array.isArray(packages) || !packages.every((p): p is string => typeof p === \"string\")) {\n throw new WorkspaceError('pnpm-workspace.yaml has a \"packages\" field that is not a list of strings.');\n }\n return packages;\n}\n\n/** Read the `workspaces` patterns from a parsed root package.json, or null. */\nfunction readManifestWorkspacePatterns(parsed: unknown): readonly string[] | null {\n if (typeof parsed !== \"object\" || parsed === null) return null;\n let field: unknown = (parsed as Record<string, unknown>)[\"workspaces\"];\n if (field === undefined) return null;\n if (typeof field === \"object\" && field !== null && !Array.isArray(field)) {\n field = (field as Record<string, unknown>)[\"packages\"];\n if (field === undefined) return null;\n }\n if (!Array.isArray(field) || !field.every((p): p is string => typeof p === \"string\")) {\n throw new WorkspaceError('package.json has a \"workspaces\" field that is not a list of strings.');\n }\n return field;\n}\n\n/**\n * Discover the manifests of a workspace root: the root package.json first,\n * then every configured workspace directory that actually contains a\n * package.json, sorted by relative path.\n *\n * @param rootDir - The workspace root (the CLI cwd).\n * @param rootManifestParsed - The JSON-parsed root package.json (already\n * loaded by the CLI), used for its `workspaces` field.\n * @returns Zero workspace matches yield just the root entry — the CLI warns\n * but proceeds.\n * @throws WorkspaceError when neither `pnpm-workspace.yaml` `packages:` nor a\n * package.json `workspaces` field exists, or either is malformed.\n */\nexport async function discoverWorkspaces(\n rootDir: string,\n rootManifestParsed: unknown,\n): Promise<readonly WorkspaceDir[]> {\n const patterns =\n (await readPnpmWorkspacePatterns(rootDir)) ?? readManifestWorkspacePatterns(rootManifestParsed);\n if (patterns === null) {\n throw new WorkspaceError(\n '--workspaces requires a \"workspaces\" field in package.json or a packages list in pnpm-workspace.yaml.',\n );\n }\n\n const result: WorkspaceDir[] = [{ dir: rootDir, manifestRelPath: \"package.json\" }];\n for (const dir of await expandWorkspacePatterns(rootDir, patterns)) {\n try {\n await stat(join(dir, \"package.json\"));\n } catch {\n continue; // matched directory without a manifest — not a workspace\n }\n result.push({ dir, manifestRelPath: `${relPosix(rootDir, dir)}/package.json` });\n }\n return result;\n}\n"],"mappings":";AASO,IAAM,UAAU;;;ACJvB,YAAY,QAAQ;AACpB,YAAY,UAAU;AAatB,IAAM,gBAAgB;AACtB,IAAM,SAAS,KAAK,KAAK,KAAK;AAM9B,SAAS,WAAW,OAA4B;AAI9C,QAAM,mBAAmB,oBAAI,IAAmB;AAAA,IAC9C;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,MAAM,MAAM;AAClB,QAAM,UAA6B;AAAA,IACjC,IAAI,IAAI;AAAA,IACR,IAAI,IAAI;AAAA,IACR,IAAI,IAAI;AAAA,IACR,IAAI,IAAI;AAAA,IACR,IAAI,OAAO;AAAA,IACX,IAAI,OAAO;AAAA,IACX,IAAI;AAAA,EACN;AAEA,SAAO,QAAQ,KAAK,CAAC,MAAM,CAAC,EAAE,SAAS,iBAAiB,IAAI,EAAE,MAAM,CAAC;AACvE;AAKA,eAAsB,cAAc,UAAsC;AACxE,QAAM,YAAiB,UAAK,UAAU,uBAAuB;AAE7D,MAAI;AACF,UAAM,UAAU,MAAS,YAAS,SAAS,WAAW,OAAO;AAC7D,UAAM,SAAS,KAAK,MAAM,OAAO;AAEjC,QAAI,OAAO,YAAY,eAAe;AACpC,aAAO,EAAE,SAAS,eAAe,SAAS,CAAC,EAAE;AAAA,IAC/C;AAEA,WAAO;AAAA,EACT,QAAQ;AAEN,WAAO,EAAE,SAAS,eAAe,SAAS,CAAC,EAAE;AAAA,EAC/C;AACF;AAKA,eAAsB,eAAe,UAAkB,OAAiC;AACtF,QAAM,YAAiB,UAAK,UAAU,uBAAuB;AAE7D,QAAS,YAAS,UAAU,WAAW,KAAK,UAAU,OAAO,MAAM,CAAC,GAAG,OAAO;AAChF;AAMO,SAAS,cACd,OACA,aACA,MAAY,oBAAI,KAAK,GACG;AACxB,QAAM,QAAQ,MAAM,QAAQ,WAAW;AAEvC,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AAEA,QAAM,MAAM,IAAI,QAAQ,IAAI,IAAI,KAAK,MAAM,SAAS,EAAE,QAAQ;AAG9D,MAAI,OAAO,UAAU,WAAW,KAAK,GAAG;AACtC,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAKO,SAAS,SACd,OACA,OACW;AACX,SAAO;AAAA,IACL,SAAS;AAAA,IACT,SAAS;AAAA,MACP,GAAG,MAAM;AAAA,MACT,CAAC,MAAM,SAAS,IAAI,GAAG;AAAA,IACzB;AAAA,EACF;AACF;AAKO,SAAS,gBAAgB,OAA4B;AAC1D,SAAO,WAAW,KAAK;AACzB;;;AClHA,eAAsB,mBACpB,OACA,IACA,cAAsB,GACR;AACd,MAAI,MAAM,WAAW,GAAG;AACtB,WAAO,CAAC;AAAA,EACV;AAGA,QAAM,UAAiB,MAAM,KAAK,EAAE,QAAQ,MAAM,OAAO,CAAC;AAC1D,MAAI,QAAQ;AAEZ,QAAM,SAAS,YAA2B;AACxC,QAAI,eAAe;AACnB,WAAO,eAAe,MAAM,QAAQ;AAClC,MAAC,QAAgB,YAAY,IAAI,MAAM,GAAG,MAAM,YAAY,CAAE;AAC9D,qBAAe;AAAA,IACjB;AAAA,EACF;AAEA,QAAM,UAAU,MAAM,KAAK,IAAI,aAAa,MAAM,MAAM,CAAC,EACtD,KAAK,IAAI,EACT,IAAI,MAAM,OAAO,CAAC;AAErB,QAAM,QAAQ,IAAI,OAAO;AACzB,SAAO;AACT;;;ACjBA,eAAsB,UACpB,KACA,UAAwB,CAAC,GACC;AAC1B,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,YAAY,QAAQ,WAAW;AACrC,QAAM,gBAAgB,WAAW,MAAM,WAAW,MAAM,GAAG,SAAS;AAEpE,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,SAAS;AAAA,QACP,cAAc;AAAA,QACd,GAAG,QAAQ;AAAA,MACb;AAAA,MACA,QAAQ,WAAW;AAAA,IACrB,CAAC;AAED,iBAAa,aAAa;AAE1B,QAAI,SAAS,WAAW,KAAK;AAC3B,aAAO,EAAE,QAAQ,YAAY;AAAA,IAC/B;AAEA,QAAI,SAAS,WAAW,OAAO,SAAS,WAAW,KAAK;AACtD,aAAO,EAAE,QAAQ,eAAe;AAAA,IAClC;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,aAAO,EAAE,QAAQ,SAAS,SAAS,QAAQ,SAAS,MAAM,GAAG;AAAA,IAC/D;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,EAAE,QAAQ,MAAM,KAAK;AAAA,EAC9B,SAAS,KAAK;AACZ,iBAAa,aAAa;AAE1B,QAAI,eAAe,SAAS,IAAI,SAAS,cAAc;AACrD,aAAO,EAAE,QAAQ,SAAS,SAAS,kBAAkB;AAAA,IACvD;AAEA,QAAI,eAAe,aAAa;AAC9B,aAAO,EAAE,QAAQ,SAAS,SAAS,wBAAwB;AAAA,IAC7D;AAEA,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,SAAS,eAAe,QAAQ,IAAI,UAAU;AAAA,IAChD;AAAA,EACF;AACF;;;AChCA,eAAsB,eACpB,cAC4D;AAC5D,MAAI,aAAa,WAAW,GAAG;AAC7B,WAAO,EAAE,QAAQ,MAAM,MAAM,CAAC,EAAE;AAAA,EAClC;AAGA,QAAM,SAAqB,CAAC;AAC5B,WAAS,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK,KAAK;AACjD,WAAO,KAAK,MAAM,KAAK,aAAa,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC;AAAA,EACxD;AAEA,QAAM,UAA+C,CAAC;AAEtD,aAAW,SAAS,QAAQ;AAC1B,UAAM,MAAM,IAAI,IAAI,mDAAmD;AACvE,QAAI,aAAa,IAAI,YAAY,MAAM,KAAK,GAAG,CAAC;AAEhD,UAAM,UAAU,MAAM,UAA+C,IAAI,SAAS,CAAC;AAEnF,QAAI,QAAQ,WAAW,MAAM;AAC3B,aAAO;AAAA,IACT;AAEA,WAAO,OAAO,SAAS,QAAQ,IAAI;AAAA,EACrC;AAEA,SAAO,EAAE,QAAQ,MAAM,MAAM,QAAQ;AACvC;AAQA,eAAsB,mBACpB,aAC+C;AAC/C,QAAM,MAAM,IAAI,IAAI,2CAA2C;AAC/D,MAAI,aAAa,IAAI,QAAQ,WAAW;AAExC,QAAM,UAAU,MAAM,UAAkC,IAAI,SAAS,CAAC;AAEtE,MAAI,QAAQ,WAAW,MAAM;AAC3B,WAAO;AAAA,EACT;AAGA,MAAI,OAAO,KAAK,QAAQ,IAAI,EAAE,WAAW,GAAG;AAC1C,WAAO,EAAE,QAAQ,MAAM,MAAM,CAAC,EAAE;AAAA,EAClC;AAEA,SAAO;AACT;AASA,eAAsB,oBACpB,cACA,cAAc,GACmC;AACjD,QAAM,WAAkB,MAAM;AAAA,IAC5B;AAAA,IACA,CAAC,SAAS,mBAAmB,IAAI;AAAA,IACjC;AAAA,EACF;AAEA,QAAM,UAAkD,CAAC;AAEzD,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,UAAM,UAAW,SAAiB,CAAC;AACnC,QAAI,SAAS,WAAW,MAAM;AAC5B,cAAS,aAAqB,CAAC,CAAC,IAAI,QAAQ;AAAA,IAC9C;AAAA,EACF;AAEA,SAAO;AACT;;;ACpFA,eAAsB,uBACpB,aAC2C;AAC3C,QAAM,UAAU,mBAAmB,WAAW;AAC9C,QAAM,MAAM,8BAA8B,OAAO;AAEjD,SAAO,UAA8B,GAAG;AAC1C;AAQA,eAAsB,oBACpB,aACyE;AACzE,QAAM,MAAM,IAAI,IAAI,wCAAwC;AAC5D,MAAI,aAAa,IAAI,QAAQ,WAAW;AACxC,MAAI,aAAa,IAAI,QAAQ,GAAG;AAEhC,QAAM,UAAU,MAAM,UAA2B,IAAI,SAAS,CAAC;AAE/D,MAAI,QAAQ,WAAW,MAAM;AAC3B,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,QAAQ,KAAK,UAAU,CAAC;AAC1C,MAAI,CAAC,WAAW;AACd,WAAO,EAAE,QAAQ,SAAS,SAAS,oBAAoB;AAAA,EACzD;AAEA,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,MAAM;AAAA,MACJ,MAAM,UAAU,QAAQ;AAAA,MACxB,MAAM,UAAU,QAAQ;AAAA,IAC1B;AAAA,EACF;AACF;AAOO,SAAS,eACd,SAC+D;AAC/D,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,EACT;AAMA,QAAM,QAAQ,QAAQ,MAAM,kDAAkD;AAC9E,MAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG;AACpC,WAAO;AAAA,EACT;AAEA,SAAO,EAAE,OAAO,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,EAAE;AAC3C;;;ACrFO,IAAM,uBAAN,MAA2B;AAAA,EACxB,UAAU;AAAA;AAAA;AAAA;AAAA,EAKlB,YAAqB;AACnB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,OAAa;AACX,SAAK,UAAU;AAAA,EACjB;AACF;AASA,eAAsB,gBACpB,OACA,MACA,OACuC;AACvC,QAAM,MAAM,gCAAgC,mBAAmB,KAAK,CAAC,IAAI,mBAAmB,IAAI,CAAC;AAEjG,QAAM,UAAkC,CAAC;AACzC,MAAI,OAAO;AACT,YAAQ,eAAe,IAAI,UAAU,KAAK;AAAA,EAC5C;AAEA,SAAO,UAA0B,KAAK,EAAE,QAAQ,CAAC;AACnD;AAOO,SAASA,gBACd,KAC+D;AAC/D,MAAI,CAAC,KAAK;AACR,WAAO;AAAA,EACT;AAOA,QAAM,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF;AAEA,MAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG;AACpC,WAAO;AAAA,EACT;AAEA,SAAO,EAAE,OAAO,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,EAAE;AAC3C;;;ACjDO,SAAS,mBAAmB,KAGnB;AACd,QAAM,EAAE,kBAAkB,iBAAiB,IAAI;AAE/C,MAAI,qBAAqB,eAAe,qBAAqB,iBAAiB;AAC5E,WAAO;AAAA,EACT;AAEA,MAAI,qBAAqB,eAAe;AACtC,WAAO;AAAA,EACT;AAGA,MAAI,iBAAiB,SAAS,iBAAiB,OAAO;AACpD,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAKA,eAAsB,mBACpB,OACA,UAA6B,CAAC,GACH;AAC3B,QAAM,WAAW,QAAQ,YAAY,QAAQ,IAAI;AACjD,QAAM,cAAc,QAAQ,eAAe;AAC3C,QAAM,cAAc,QAAQ,eAAe,QAAQ,IAAI;AAEvD,QAAM,WAAgC,CAAC;AACvC,QAAM,WAAiC,CAAC;AAGxC,MAAI,QAAQ,EAAE,SAAS,GAAY,SAAS,CAAC,EAAE;AAE/C,MAAI,CAAC,QAAQ,SAAS;AACpB,QAAI;AACF,cAAQ,MAAM,cAAc,QAAQ;AAAA,IACtC,QAAQ;AACN,eAAS,KAAK;AAAA,QACZ,QAAQ;AAAA,QACR,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAAA,EACF;AAEA,QAAM,SAA+B,CAAC;AACtC,QAAM,UAAoB,CAAC;AAE3B,aAAW,QAAQ,OAAO;AACxB,UAAM,QAAQ,QAAQ,UAAU,SAAY,cAAc,OAAO,IAAI;AACrE,QAAI,OAAO;AACT,aAAO,KAAK,MAAM,QAAQ;AAAA,IAC5B,OAAO;AACL,cAAQ,KAAK,IAAI;AAAA,IACnB;AAAA,EACF;AAGA,WAAS,KAAK,GAAG,MAAM;AAEvB,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,EAAE,cAAc,UAAU,SAAS;AAAA,EAC5C;AAGA,QAAM,wBAAwB,MAAM,eAAe,OAAO;AAC1D,QAAM,qBAA2C,sBAAsB,WAAW,OAC9E,sBAAsB,OACtB,CAAC;AAEL,MAAI,sBAAsB,WAAW,SAAS;AAC5C,aAAS,KAAK;AAAA,MACZ,QAAQ;AAAA,MACR,SAAS,iCAAiC,sBAAsB,OAAO;AAAA,IACzE,CAAC;AAAA,EACH;AAGA,QAAM,eAAe,MAAM;AAAA,IACzB;AAAA,IACA,OAAO,SAAS;AACd,YAAM,UAAU,MAAM,uBAAuB,IAAI;AACjD,aAAO,EAAE,MAAM,QAAQ;AAAA,IACzB;AAAA,IACA;AAAA,EACF;AAEA,QAAM,UAA+B,CAAC;AACtC,aAAW,EAAE,MAAM,QAAQ,KAAK,cAAc;AAC5C,QAAI,QAAQ,WAAW,MAAM;AAC3B,MAAC,QAAgB,IAAI,IAAI,QAAQ;AAAA,IACnC,WAAW,QAAQ,WAAW,aAAa;AACzC,MAAC,QAAgB,IAAI,IAAI,EAAE,OAAO,MAAM;AAAA,IAC1C,WAAW,QAAQ,WAAW,SAAS;AACrC,MAAC,QAAgB,IAAI,IAAI,EAAE,OAAO,QAAQ,QAAQ;AAAA,IACpD,WAAW,QAAQ,WAAW,gBAAgB;AAC5C,MAAC,QAAgB,IAAI,IAAI,EAAE,aAAa,KAAK;AAAA,IAC/C;AAAA,EACF;AAGA,QAAM,YAAY,QAAQ,OAAO,CAAC,SAAS,mBAAmB,IAAI,CAAC;AACnE,QAAM,mBAAmB,OAAO,UAAU,SAAS,IAAI,oBAAoB,WAAW,WAAW,IAAI,QAAQ,QAAQ,CAAC,CAAC;AAGvH,QAAM,eAAe,QAAQ,OAAO,CAAC,SAAS,CAAC,mBAAmB,IAAI,CAAC;AACvE,QAAM,mBAA+E,CAAC;AAEtF,aAAW,QAAQ,cAAc;AAC/B,UAAM,UAAU,MAAM,oBAAoB,IAAI;AAC9C,QAAI,QAAQ,WAAW,QAAQ,QAAQ,KAAK,SAAS,MAAM;AACzD,uBAAiB,IAAI,IAAI,QAAQ;AAAA,IACnC;AAAA,EACF;AAGA,QAAM,gBAAgB,IAAI,qBAAqB;AAC/C,QAAM,cAID,CAAC;AAGN,QAAM,sBAAsB,oBAAI,IAAY;AAE5C,aAAW,QAAQ,SAAS;AAC1B,QAAI;AAGJ,UAAM,YAAa,iBAAyB,IAAI;AAChD,QAAI,aAAa,OAAO,cAAc,YAAY,eAAe,WAAW;AAC1E,gBAAW,UAAkB;AAAA,IAC/B;AAGA,QAAI,CAAC,WAAY,QAAgB,IAAI,GAAG,YAAY,KAAK;AACvD,gBAAW,QAAgB,IAAI,EAAE,WAAW;AAAA,IAC9C;AAEA,QAAI,CAAC,SAAS;AACZ;AAAA,IACF;AAEA,wBAAoB,IAAI,IAAI;AAG5B,QAAI,SAASC,gBAAqB,OAAO;AACzC,QAAI,CAAC,QAAQ;AACX,eAAS,eAAkB,OAAO;AAAA,IACpC;AAEA,QAAI,QAAQ;AACV,kBAAY,KAAK,EAAE,MAAM,OAAO,OAAO,OAAO,MAAM,OAAO,KAAK,CAAC;AAAA,IACnE;AAAA,EACF;AAGA,QAAM,aAAkC,CAAC;AAEzC,aAAW,EAAE,MAAM,OAAO,KAAK,KAAK,aAAa;AAC/C,QAAI,cAAc,UAAU,GAAG;AAC7B;AAAA,IACF;AAEA,UAAM,UAAU,MAAM,gBAAgB,OAAO,MAAM,WAAW;AAE9D,QAAI,QAAQ,WAAW,MAAM;AAC3B,iBAAW,IAAI,IAAI,QAAQ;AAAA,IAC7B,WAAW,QAAQ,WAAW,gBAAgB;AAC5C,oBAAc,KAAK;AACnB,eAAS,KAAK;AAAA,QACZ,QAAQ;AAAA,QACR,SAAS;AAAA,MACX,CAAC;AACD;AAAA,IACF,WAAW,QAAQ,WAAW,SAAS;AACrC,MAAC,WAAmB,IAAI,IAAI,EAAE,OAAO,QAAQ,QAAQ;AAAA,IACvD;AAAA,EACF;AAGA,aAAW,QAAQ,SAAS;AAC1B,UAAM,MAAO,QAAgB,IAAI,KAAK,CAAC;AACvC,UAAM,WAAY,mBAA2B,IAAI,KAAK,CAAC;AAGvD,UAAM,oBAAoB,OAAO,UAAU,eAAe,KAAK,oBAAoB,IAAI;AACvF,UAAM,YAAa,iBAAyB,IAAI,KAAK,CAAC;AACtD,UAAM,SAAU,WAAmB,IAAI;AAGvC,UAAM,WAAW,IAAI,UAAU,SAAS,CAAC,IAAI,eAAe,CAAC,IAAI;AACjE,UAAM,cAAc,IAAI,UAAU;AAGlC,UAAM,aAAsE,WACxE;AAAA,MACE,OAAO;AAAA,MACP,OAAO;AAAA,QACL,YAAY,QAAQ,IAAI,UAAU;AAAA,QAClC,SAAS,IAAI,aAAa,OAAO,IAAI,UAAU,IAAI;AAAA,MACrD;AAAA,MACA,QAAQ;AAAA,IACV,IACA,EAAE,OAAO,OAAO,QAAQ,cAAc,kBAAkB,oBAAoB;AAEhF,UAAM,mBAAoC,WACtC,EAAE,OAAO,MAAM,OAAO,QAAQ,IAAI,aAAa,GAAG,QAAQ,MAAM,IAChE,EAAE,OAAO,OAAO,QAAQ,cAAc,kBAAkB,oBAAoB;AAEhF,UAAM,wBAAyC,WAC3C;AAAA,MACE,OAAO;AAAA,MACP,OAAO,QAAQ,IAAI,mBAAmB,cAAc,CAAC;AAAA,MACrD,QAAQ;AAAA,IACV,IACA,EAAE,OAAO,OAAO,QAAQ,cAAc,kBAAkB,oBAAoB;AAGhF,UAAM,oBAAqC,WACvC;AAAA,MACE,OAAO;AAAA,MACP,OAAO;AAAA,QACL,IAAI,SAAS,MAAM,QAAQ,IAAI,KAAK,KAAK,IAAI,MAAM,KAAK,CAAC,MAAc,yBAAyB,KAAK,CAAC,CAAC;AAAA,MACzG;AAAA,MACA,QAAQ;AAAA,IACV,IACA,EAAE,OAAO,OAAO,QAAQ,cAAc,kBAAkB,oBAAoB;AAGhF,QAAI;AACJ,QAAI;AACJ,QAAI,eAA2D;AAE/D,QAAI,UAAU,CAAC,OAAO,OAAO;AAC3B,uBAAiB,EAAE,OAAO,MAAM,OAAO,OAAO,aAAa,MAAM,QAAQ,aAAa;AACtF,UAAI,OAAO,WAAW;AACpB,yBAAiB,EAAE,OAAO,MAAM,OAAO,OAAO,WAAW,QAAQ,aAAa;AAAA,MAChF,OAAO;AACL,yBAAiB,EAAE,OAAO,OAAO,QAAQ,eAAe;AAAA,MAC1D;AACA,qBAAe;AAAA,IACjB,WAAW,aAAa,OAAO,cAAc,YAAY,YAAY,WAAW;AAC9E,YAAM,MAAM;AACZ,UAAI,IAAI,QAAQ,eAAe,QAAW;AACxC,yBAAiB,EAAE,OAAO,MAAM,OAAO,IAAI,OAAO,YAAY,QAAQ,qBAAqB;AAC3F,YAAI,IAAI,OAAO,OAAO,UAAU;AAC9B,2BAAiB,EAAE,OAAO,MAAM,OAAO,IAAI,OAAO,MAAM,UAAU,QAAQ,qBAAqB;AAAA,QACjG,OAAO;AACL,2BAAiB,EAAE,OAAO,OAAO,QAAQ,eAAe;AAAA,QAC1D;AACA,uBAAe;AAAA,MACjB,OAAO;AACL,yBAAiB,EAAE,OAAO,OAAO,QAAQ,cAAc,UAAU,IAAI,wBAAwB,kBAAkB;AAC/G,yBAAiB,EAAE,OAAO,OAAO,QAAQ,cAAc,UAAU,IAAI,wBAAwB,kBAAkB;AAAA,MACjH;AAAA,IACF,OAAO;AACL,YAAM,iBAAgC,cAAc,UAAU,IAC1D,wBACA,oBAAoB,IAAI,IAAI,IAC1B,oBACA;AACN,uBAAiB,EAAE,OAAO,OAAO,QAAQ,eAAe;AACxD,uBAAiB,EAAE,OAAO,OAAO,QAAQ,eAAe;AAAA,IAC1D;AAGA,QAAI,YAA2B;AAC/B,QAAI,aAAa,OAAO,cAAc,YAAY,eAAe,WAAW;AAC1E,kBAAa,UAAkB,aAAa;AAAA,IAC9C,WAAW,IAAI,YAAY,KAAK;AAC9B,YAAM,SAAS,eAAkB,IAAI,WAAW,GAAG,KAAKA,gBAAqB,IAAI,WAAW,GAAG;AAC/F,UAAI,QAAQ;AACV,oBAAY,sBAAsB,OAAO,KAAK,IAAI,OAAO,IAAI;AAAA,MAC/D;AAAA,IACF;AAGA,QAAI;AAEJ,QAAI,aAAa,OAAO,cAAc,YAAY,SAAS,WAAW;AACpE,YAAM,MAAM;AACZ,UAAI,IAAI,KAAK,mBAAmB;AAC9B,sBAAc,EAAE,OAAO,MAAM,OAAO,EAAE,MAAM,IAAI,IAAI,kBAAkB,GAAG,QAAQ,YAAY;AAAA,MAC/F,WAAW,iBAAiB,IAAI,GAAG,MAAM;AACvC,sBAAc,EAAE,OAAO,MAAM,OAAO,EAAE,MAAM,iBAAiB,IAAI,EAAE,KAAK,GAAG,QAAQ,aAAa;AAAA,MAClG,OAAO;AACL,sBAAc,EAAE,OAAO,OAAO,QAAQ,mBAAmB;AAAA,MAC3D;AAAA,IACF,WAAW,iBAAiB,IAAI,GAAG,MAAM;AACvC,oBAAc,EAAE,OAAO,MAAM,OAAO,EAAE,MAAM,iBAAiB,IAAI,EAAE,KAAK,GAAG,QAAQ,aAAa;AAAA,IAClG,OAAO;AACL,oBAAc,EAAE,OAAO,OAAO,QAAQ,mBAAmB;AAAA,IAC3D;AAGA,UAAM,kBAAoF,CAAC;AAC3F,QAAI,mBAAmB;AACrB,sBAAgB,KAAK,kBAAkB;AAAA,IACzC;AACA,QAAI,sBAAsB,SAAS,sBAAsB,OAAO;AAC9D,sBAAgB,KAAK,iBAAiB;AAAA,IACxC;AACA,QAAI,kBAAkB,SAAS,kBAAkB,OAAO;AACtD,sBAAgB,KAAK,mBAAmB;AAAA,IAC1C;AAEA,UAAM,aAAa,qBAAsB,sBAAsB,SAAS,sBAAsB,SAC3F,kBAAkB,SAAS,kBAAkB;AAGhD,UAAM,cAAc,mBAAmB;AAAA,MACrC,kBAAmB,SAAS,mBAA2B;AAAA,MACvD;AAAA,IACF,CAAC;AAGD,UAAM,cAAmC,CAAC;AAC1C,QAAI,aAAa;AACf,kBAAY,KAAK;AAAA,QACf,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AACA,QAAI,IAAI,OAAO;AACb,kBAAY,KAAK;AAAA,QACf,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ,SAAS,6BAA6B,IAAI,KAAK;AAAA,MACjD,CAAC;AAAA,IACH;AACA,QAAI,QAAQ,OAAO;AACjB,kBAAY,KAAK;AAAA,QACf,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ,SAAS,gCAAgC,OAAO,KAAK;AAAA,MACvD,CAAC;AAAA,IACH;AAEA,UAAM,cAAkC;AAAA,MACtC;AAAA,MACA,UAAU;AAAA,MACV,KAAK;AAAA,QACH,OAAO,YAAY,CAAC;AAAA,QACpB,eAAe,WAAW,IAAI,UAAU;AAAA,QACxC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,eAAe,IAAI,YAAY,MAAM,OAAO,IAAI,WAAW,GAAG,EAAE,QAAQ,UAAU,EAAE,IAAI;AAAA,MAC1F;AAAA,MACA,WAAW;AAAA,QACT,QAAQ;AAAA,QACR,cAAc,QAAQ,SAAS,YAAY;AAAA,QAC3C,oBAAqB,SAAS,mBAA2B;AAAA;AAAA,QAEzD,WAAW,aAAa,OAAO,cAAc,WAAY,UAAkB,aAAa,OAAO;AAAA,QAC/F,iBACG,aAAa,OAAO,cAAc,YAAa,UAAkB,KAAK,qBAAsB;AAAA;AAAA,QAE/F,gBACE,aAAa,OAAO,cAAc,YAAY,OAAQ,UAAkB,QAAQ,eAAe,YAC1F,UAAkB,OAAO,aAC1B;AAAA,QACN,gBACG,aAAa,OAAO,cAAc,YAAa,UAAkB,QAAQ,OAAO,YAAa;AAAA,QAChG,wBACG,aAAa,OAAO,cAAc,YAAa,UAAkB,0BAA2B,CAAC;AAAA,MAClG;AAAA,MACA,QAAQ;AAAA,QACN,UAAU;AAAA,QACV,UAAU;AAAA,QACV,SAAS;AAAA,QACT,QAAQ;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS;AAAA,QACP,MAAM;AAAA,QACN,UAAU;AAAA,UACR,kBAAmB,SAAS,mBAA2B;AAAA,UACvD,kBAAkB,iBAAiB,QAAQ,iBAAiB,QAAQ;AAAA,QACtE;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAEA,aAAS,KAAK,WAAW;AAIzB,QAAI,CAAC,QAAQ,SAAS;AACpB,YAAM,YAAY,EAAE,YAAW,oBAAI,KAAK,GAAE,YAAY,GAAG,UAAU,YAAY;AAC/E,UAAI,CAAC,gBAAgB,SAAS,GAAG;AAC/B,gBAAQ,SAAS,OAAO,SAAS;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AAGA,MAAI,CAAC,QAAQ,WAAW,QAAQ,SAAS,GAAG;AAC1C,QAAI;AACF,YAAM,eAAe,UAAU,KAAK;AAAA,IACtC,QAAQ;AACN,eAAS,KAAK;AAAA,QACZ,QAAQ;AAAA,QACR,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO,EAAE,cAAc,UAAU,SAAS;AAC5C;;;AC/WO,IAAM,iBAAyB;AAAA,EACpC,OAAO;AAAA,IACL,iBAAiB;AAAA,IACjB,gBAAgB;AAAA,IAChB,aAAa,EAAE,YAAY,IAAI,aAAa,GAAG;AAAA,IAC/C,gBAAgB;AAAA,IAChB,eAAe;AAAA,IACf,uBAAuB;AAAA,EACzB;AAAA,EACA,OAAO;AAAA,EACP,OAAO,CAAC;AACV;AAsCA,IAAM,gBAAiB,WAAW,KAAM,KAAK,KAAK,KAAK;AAQvD,SAAS,cAAc,MAAc,OAA8B,KAAuB;AACxF,QAAM,QAAQ,MAAM,KAAK,CAAC,MAAM,EAAE,YAAY,IAAI;AAClD,MAAI,CAAC,MAAO,QAAO,EAAE,MAAM,OAAO;AAClC,MAAI,MAAM,YAAY,KAAM,QAAO,EAAE,MAAM,UAAU,MAAM;AAE3D,QAAM,YAAY,KAAK,MAAM,GAAG,MAAM,OAAO,gBAAgB;AAC7D,SAAO,IAAI,QAAQ,KAAK,YAAY,EAAE,MAAM,UAAU,MAAM,IAAI,EAAE,MAAM,WAAW,MAAM;AAC3F;AAEA,SAAS,UAAU,MAAsB;AACvC,SACE,sEACc,IAAI;AAEtB;AAEA,SAAS,aAAa,MAAsB;AAC1C,SAAO,yCAAyC,mBAAmB,IAAI,CAAC;AAC1E;AAEA,SAAS,OAAO,MAAsB;AACpC,SAAO,iCAAiC,IAAI;AAC9C;AAYA,SAAS,cAAc,KAAyB,OAAoB,KAAsB;AACxF,QAAM,OAAkB,CAAC;AACzB,QAAM,OAAO,IAAI;AAGjB,MAAI,MAAM,oBAAoB,OAAO;AACnC,QAAI,IAAI,QAAQ,SAAS,eAAe;AACtC,WAAK,KAAK;AAAA,QACR,MAAM;AAAA,QACN,UAAU,MAAM;AAAA,QAChB,SACE,GAAG,IAAI,4KAE0C,UAAU,IAAI,CAAC;AAAA,QAClE,aAAa,aAAa,IAAI;AAAA,MAChC,CAAC;AAAA,IACH,WAAW,IAAI,QAAQ,SAAS,gBAAgB;AAC9C,WAAK,KAAK;AAAA,QACR,MAAM;AAAA,QACN,UAAU;AAAA,QACV,SACE,GAAG,IAAI;AAAA,QAGT,aAAa,OAAO,IAAI;AAAA,MAC1B,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,MAAM,mBAAmB,SAAS,IAAI,QAAQ,SAAS,WAAW;AACpE,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,UAAU,MAAM;AAAA,MAChB,SACE,gCAAgC,IAAI,yJAEU,UAAU,IAAI,CAAC;AAAA,MAC/D,aAAa,aAAa,IAAI;AAAA,IAChC,CAAC;AAAA,EACH;AAGA,MAAI,MAAM,gBAAgB,SAAS,IAAI,YAAY,OAAO;AACxD,UAAM,cAAc,KAAK,MAAM,IAAI,YAAY,MAAM,IAAI;AACzD,QAAI,CAAC,OAAO,MAAM,WAAW,GAAG;AAC9B,YAAM,aAAa,IAAI,QAAQ,IAAI,eAAe;AAClD,YAAM,EAAE,YAAY,YAAY,IAAI,MAAM;AAC1C,YAAM,OACJ,aAAa,cAAc,UAAU,aAAa,aAAa,SAAS;AAC1E,UAAI,SAAS,MAAM;AACjB,cAAM,YAAY,SAAS,UAAU,cAAc;AACnD,aAAK,KAAK;AAAA,UACR,MAAM;AAAA,UACN,UAAU;AAAA,UACV,SACE,GAAG,IAAI,uBAAuB,OAAO,KAAK,MAAM,SAAS,CAAC,CAAC,gBACvD,IAAI,YAAY,MAAM,KAAK,MAAM,GAAG,EAAE,CAAC,oBACxC,OAAO,SAAS,CAAC,yHAEpB,UAAU,IAAI;AAAA,UAChB,aAAa,OAAO,IAAI;AAAA,QAC1B,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAGA,MAAI,MAAM,mBAAmB,SAAS,IAAI,OAAO,SAAS,SAAS,IAAI,OAAO,SAAS,OAAO;AAC5F,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,UAAU,MAAM;AAAA,MAChB,SACE,6BAA6B,IAAI,uFACgB,UAAU,IAAI,CAAC;AAAA,MAClE,aAAa,IAAI,OAAO,WAAW,OAAO,IAAI;AAAA,IAChD,CAAC;AAAA,EACH;AAGA,MAAI,MAAM,kBAAkB,SAAS,IAAI,IAAI,WAAW,SAAS,IAAI,IAAI,WAAW,MAAM,YAAY;AACpG,UAAM,WAAW,IAAI,IAAI,WAAW,MAAM;AAC1C,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,UAAU,MAAM;AAAA,MAChB,SACE,GAAG,IAAI,2BACN,WAAW,MAAM,QAAQ,MAAM,MAChC,+CAA+C,UAAU,IAAI,CAAC;AAAA,MAChE,aAAa,OAAO,IAAI;AAAA,IAC1B,CAAC;AAAA,EACH;AAGA,MAAI,MAAM,0BAA0B,SAAS,IAAI,UAAU,UAAU,IAAI,UAAU,cAAc;AAC/F,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,UAAU,MAAM;AAAA,MAChB,SACE,GAAG,IAAI,kGACyB,UAAU,IAAI,CAAC;AAAA,MACjD,aAAa,aAAa,IAAI;AAAA,IAChC,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAmBO,SAAS,eACd,cACA,QACA,UAA2B,CAAC,GACR;AACpB,QAAM,MAAM,QAAQ,OAAO,oBAAI,KAAK;AACpC,QAAM,WAAsB,CAAC;AAE7B,aAAW,OAAO,cAAc;AAC9B,QAAI,OAAO,UAAU,oBAAoB,CAAC,IAAI,WAAY;AAE1D,UAAM,aAAa,cAAc,IAAI,MAAM,OAAO,OAAO,GAAG;AAE5D,eAAW,OAAO,cAAc,KAAK,OAAO,OAAO,GAAG,GAAG;AACvD,UAAI,IAAI,aAAa,UAAU,WAAW,SAAS,QAAQ;AACzD,iBAAS,KAAK,EAAE,SAAS,IAAI,MAAM,GAAG,KAAK,cAAc,KAAK,CAAC;AAAA,MACjE,WAAW,WAAW,SAAS,UAAU;AACvC,iBAAS,KAAK;AAAA,UACZ,SAAS,IAAI;AAAA,UACb,GAAG;AAAA,UACH,cAAc;AAAA,YACZ,QAAQ,WAAW,MAAM;AAAA,YACzB,SAAS,WAAW,MAAM;AAAA,UAC5B;AAAA,QACF,CAAC;AAAA,MACH,OAAO;AAEL,cAAM,EAAE,MAAM,IAAI;AAClB,iBAAS,KAAK;AAAA,UACZ,SAAS,IAAI;AAAA,UACb,MAAM,IAAI;AAAA,UACV,UAAU;AAAA,UACV,SACE,GAAG,IAAI,OAAO,kCAAkC,IAAI,IAAI,MACvD,MAAM,SAAS,MAAM,MAAM,MAAM,OAAO,MACzC,eAAe,MAAM,WAAW,GAAG;AAAA,UAErC,aAAa,IAAI;AAAA,UACjB,cAAc;AAAA,QAChB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;ACjVA,SAAS,gBAAgB;AACzB,SAAS,eAAe;AACxB,SAAS,aAAa;AAMf,IAAM,0BAA0B;AAMhC,IAAM,cAAN,cAA0B,MAAM;AAAA,EACrC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAEA,IAAM,kBAA2C,CAAC,SAAS,QAAQ,KAAK;AACxE,IAAM,SAAiC,CAAC,kBAAkB,UAAU;AACpE,IAAM,YAA4C;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AACA,IAAM,iBAAiB,CAAC,SAAS,SAAS,OAAO;AAEjD,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,OAAO,OAA6C;AAC3D,SAAO,MAAM,QAAQ,KAAK;AAC5B;AAEA,SAAS,KAAK,OAAe,SAAwB;AACnD,QAAM,IAAI,YAAY,iBAAiB,KAAK,KAAK,OAAO,EAAE;AAC5D;AAEA,SAAS,cAAc,OAAgB,KAAa,OAA6B;AAC/E,MAAI,OAAO,UAAU,YAAa,gBAAsC,SAAS,KAAK,GAAG;AACvF,WAAO;AAAA,EACT;AACA,SAAO,KAAK,OAAO,SAAS,GAAG,mBAAmB,gBAAgB,KAAK,KAAK,CAAC,SAAS,KAAK,UAAU,KAAK,CAAC,EAAE;AAC/G;AAEA,SAAS,iBAAiB,OAAgB,OAA2C;AACnF,MAAI,UAAU,MAAO,QAAO;AAC5B,MAAI,SAAS,KAAK,GAAG;AACnB,UAAM,OAAO,OAAO,KAAK,KAAK;AAC9B,UAAM,UAAU,KAAK,OAAO,CAAC,MAAM,MAAM,gBAAgB,MAAM,aAAa;AAC5E,QAAI,QAAQ,SAAS,GAAG;AACtB,WAAK,OAAO,yCAAyC,QAAQ,KAAK,IAAI,CAAC,qCAAqC;AAAA,IAC9G;AACA,UAAM,EAAE,YAAY,YAAY,IAAI;AACpC,QAAI,OAAO,eAAe,YAAY,OAAO,gBAAgB,UAAU;AACrE,WAAK,OAAO,oEAAoE;AAAA,IAClF;AACA,QAAI,aAAa,KAAK,cAAc,KAAK,cAAc,YAAY;AACjE,WAAK,OAAO,0EAA0E;AAAA,IACxF;AACA,WAAO,EAAE,YAAY,YAAY;AAAA,EACnC;AACA,SAAO,KAAK,OAAO,uEAAuE,KAAK,UAAU,KAAK,CAAC,EAAE;AACnH;AAEA,SAAS,WAAW,OAAgB,OAA4B;AAC9D,MAAI,CAAC,SAAS,KAAK,GAAG;AACpB,WAAO,KAAK,OAAO,kCAAkC,KAAK,UAAU,KAAK,CAAC,EAAE;AAAA,EAC9E;AACA,QAAM,UAAU,OAAO,KAAK,KAAK,EAAE,OAAO,CAAC,MAAM,CAAE,UAAgC,SAAS,CAAC,CAAC;AAC9F,MAAI,QAAQ,SAAS,GAAG;AACtB,SAAK,OAAO,oBAAoB,QAAQ,KAAK,IAAI,CAAC,kBAAkB,UAAU,KAAK,IAAI,CAAC,GAAG;AAAA,EAC7F;AACA,QAAM,WAAW,eAAe;AAChC,QAAM,WAAW,CAAC,KAAwB,aACxC,OAAO,QAAQ,cAAc,MAAM,GAAG,GAAG,KAAK,KAAK,IAAI;AACzD,SAAO;AAAA,IACL,iBAAiB,SAAS,mBAAmB,SAAS,eAAe;AAAA,IACrE,gBAAgB,SAAS,kBAAkB,SAAS,cAAc;AAAA,IAClE,aAAa,iBAAiB,QAAQ,iBAAiB,MAAM,aAAa,KAAK,IAAI,SAAS;AAAA,IAC5F,gBAAgB,SAAS,kBAAkB,SAAS,cAAc;AAAA,IAClE,eAAe,SAAS,iBAAiB,SAAS,aAAa;AAAA,IAC/D,uBAAuB,SAAS,yBAAyB,SAAS,qBAAqB;AAAA,EACzF;AACF;AAEA,IAAM,kBAAkB;AAExB,SAAS,gBAAgB,OAAgB,OAAe,OAA2B;AACjF,QAAM,KAAK,SAAS,OAAO,KAAK,CAAC;AACjC,MAAI,CAAC,SAAS,KAAK,GAAG;AACpB,WAAO,KAAK,OAAO,GAAG,EAAE,gDAAgD,KAAK,UAAU,KAAK,CAAC,EAAE;AAAA,EACjG;AACA,QAAM,UAAU,OAAO,KAAK,KAAK,EAAE,OAAO,CAAC,MAAM,MAAM,aAAa,MAAM,YAAY,MAAM,SAAS;AACrG,MAAI,QAAQ,SAAS,GAAG;AACtB,SAAK,OAAO,GAAG,EAAE,wBAAwB,QAAQ,KAAK,IAAI,CAAC,sCAAsC;AAAA,EACnG;AACA,QAAM,MAAM,MAAM;AAClB,MAAI,OAAO,QAAQ,YAAY,IAAI,WAAW,GAAG;AAC/C,SAAK,OAAO,GAAG,EAAE,qCAAqC;AAAA,EACxD;AACA,QAAM,SAAS,MAAM;AACrB,MAAI,WAAW,UAAa,OAAO,WAAW,UAAU;AACtD,SAAK,OAAO,GAAG,EAAE,uCAAuC;AAAA,EAC1D;AACA,MAAI,UAAU,MAAM;AAEpB,MAAI,mBAAmB,QAAQ,CAAC,OAAO,MAAM,QAAQ,QAAQ,CAAC,GAAG;AAC/D,cAAU,QAAQ,YAAY,EAAE,MAAM,GAAG,EAAE;AAAA,EAC7C;AACA,MAAI,YAAY,WAAc,OAAO,YAAY,YAAY,CAAC,gBAAgB,KAAK,OAAO,IAAI;AAC5F,SAAK,OAAO,GAAG,EAAE,wDAAwD,KAAK,UAAU,MAAM,OAAO,CAAC,EAAE;AAAA,EAC1G;AACA,SAAO;AAAA,IACL,SAAS;AAAA,IACT,QAAQ,OAAO,WAAW,WAAW,SAAS;AAAA,IAC9C,SAAS,OAAO,YAAY,WAAW,UAAU;AAAA,EACnD;AACF;AAYO,SAAS,YAAY,UAAkB,UAA2B;AACvE,QAAM,QAAQ,WAAW,KAAK,QAAQ,MAAM;AAE5C,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,QAAQ;AAAA,EACtB,SAAS,KAAK;AACZ,WAAO,KAAK,OAAO,4BAAuB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAAA,EAC9F;AAEA,MAAI,QAAQ,QAAQ,QAAQ,OAAW,QAAO;AAC9C,MAAI,CAAC,SAAS,GAAG,GAAG;AAClB,WAAO,KAAK,OAAO,oCAAoC,KAAK,UAAU,GAAG,CAAC,EAAE;AAAA,EAC9E;AAEA,QAAM,UAAU,OAAO,KAAK,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,eAAe,SAAS,CAAC,CAAC;AAC1E,MAAI,QAAQ,SAAS,GAAG;AACtB,SAAK,OAAO,6BAA6B,QAAQ,KAAK,IAAI,CAAC,cAAc,eAAe,KAAK,IAAI,CAAC,GAAG;AAAA,EACvG;AAEA,QAAM,QAAQ,WAAW,MAAM,WAAW,IAAI,OAAO,KAAK,IAAI,eAAe;AAE7E,MAAI,QAAqB,eAAe;AACxC,MAAI,WAAW,KAAK;AAClB,UAAM,QAAQ,IAAI;AAClB,QAAI,OAAO,UAAU,YAAY,CAAE,OAA6B,SAAS,KAAK,GAAG;AAC/E,WAAK,OAAO,0BAA0B,OAAO,KAAK,KAAK,CAAC,SAAS,KAAK,UAAU,KAAK,CAAC,EAAE;AAAA,IAC1F;AACA,YAAQ;AAAA,EACV;AAEA,MAAI,QAA+B,eAAe;AAClD,MAAI,WAAW,KAAK;AAClB,UAAM,QAAQ,IAAI;AAClB,QAAI,CAAC,OAAO,KAAK,GAAG;AAClB,aAAO,KAAK,OAAO,0CAA0C,KAAK,UAAU,KAAK,CAAC,EAAE;AAAA,IACtF;AACA,YAAQ,MAAM,IAAI,CAAC,OAAO,MAAM,gBAAgB,OAAO,GAAG,KAAK,CAAC;AAAA,EAClE;AAEA,SAAO,EAAE,OAAO,OAAO,MAAM;AAC/B;AAgBA,eAAsB,WAAWC,OAAe,MAAc,QAAQ,IAAI,GAAoB;AAC5F,QAAM,WAAWA,UAAS;AAC1B,QAAM,WAAW,WAAW,QAAQ,KAAKA,KAAI,IAAI,QAAQ,KAAK,uBAAuB;AAErF,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,SAAS,UAAU,MAAM;AAAA,EACxC,SAAS,KAAK;AACZ,UAAM,OAAQ,IAA8B;AAC5C,QAAI,SAAS,YAAY,CAAC,SAAU,QAAO;AAC3C,UAAM,IAAI;AAAA,MACR,YAAY,SAAS,WACjB,0BAA0B,QAAQ,+BAClC,8BAA8B,QAAQ,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IACjG;AAAA,EACF;AAEA,SAAO,YAAY,MAAM,QAAQ;AACnC;;;ACjLO,SAAS,eACd,UACA,MAC0B;AAC1B,SAAO,SAAS,IAAI,CAAC,OAAO,EAAE,GAAG,GAAG,KAAK,EAAE;AAC7C;AAiBO,SAAS,UAAU,UAA8C;AACtE,MAAI,SAAS;AACb,MAAI,WAAW;AACf,MAAI,QAAQ;AACZ,MAAI,aAAa;AACjB,aAAW,KAAK,UAAU;AACxB,QAAI,EAAE,iBAAiB,MAAM;AAC3B;AAAA,IACF,WAAW,EAAE,aAAa,SAAS;AACjC;AAAA,IACF,WAAW,EAAE,aAAa,QAAQ;AAChC;AAAA,IACF,OAAO;AACL;AAAA,IACF;AAAA,EACF;AACA,SAAO,EAAE,QAAQ,UAAU,OAAO,WAAW;AAC/C;AAWO,SAAS,gBAAgB,UAAqC;AACnE,SAAO,SAAS,KAAK,CAAC,MAAM,EAAE,aAAa,WAAW,EAAE,iBAAiB,IAAI,IAAI,IAAI;AACvF;;;AC3EO,SAAS,WAAW,QAAwB;AACjD,QAAM,UAAU,UAAU,OAAO,QAAQ;AAEzC,QAAM,MAAM;AAAA,IACV,SAAS;AAAA,IACT,SAAS;AAAA,MACP,SAAS,OAAO;AAAA,MAChB,QAAQ,QAAQ;AAAA,MAChB,UAAU,QAAQ;AAAA,MAClB,OAAO,QAAQ;AAAA,MACf,YAAY,QAAQ;AAAA,IACtB;AAAA,IACA,UAAU,OAAO,SAAS,IAAI,CAAC,OAAO;AAAA,MACpC,MAAM,EAAE;AAAA,MACR,SAAS,EAAE;AAAA,MACX,MAAM,EAAE;AAAA,MACR,UAAU,EAAE;AAAA,MACZ,SAAS,EAAE;AAAA,MACX,aAAa,EAAE;AAAA,MACf,cAAc,EAAE,iBAAiB,OAC7B,OACA,EAAE,QAAQ,EAAE,aAAa,QAAQ,SAAS,EAAE,aAAa,QAAQ;AAAA,IACvE,EAAE;AAAA,IACF,UAAU,OAAO,SAAS,IAAI,CAAC,OAAO;AAAA,MACpC,GAAI,EAAE,eAAe,SAAY,EAAE,YAAY,EAAE,WAAW,IAAI,CAAC;AAAA,MACjE,QAAQ,EAAE;AAAA,MACV,SAAS,EAAE;AAAA,IACb,EAAE;AAAA,EACJ;AAEA,SAAO,GAAG,KAAK,UAAU,KAAK,MAAM,CAAC,CAAC;AAAA;AACxC;;;AC1CA,IAAM,kBAAkB;AAGxB,IAAM,WAA8B;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,oBAAsD;AAAA,EAC1D,iBACE;AAAA,EACF,gBACE;AAAA,EACF,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,uBAAuB;AACzB;AAGA,SAAS,WAAW,UAA6D;AAC/E,SAAO,aAAa,SAAS,YAAY;AAC3C;AA6BO,SAAS,YAAY,QAAgB,UAAwB,CAAC,GAAW;AAC9E,QAAM,SAAS,QAAQ,WAAW,MAAM;AACxC,QAAM,UAAU,UAAU,OAAO,QAAQ;AAEzC,QAAM,UAAU,OAAO,SAAS,IAAI,CAAC,MAAM;AACzC,UAAM,OAAO,OAAO,EAAE,MAAM,EAAE,OAAO;AACrC,WAAO;AAAA,MACL,QAAQ,EAAE;AAAA,MACV,WAAW,SAAS,QAAQ,EAAE,IAAI;AAAA,MAClC,OAAO,WAAW,EAAE,QAAQ;AAAA,MAC5B,SAAS,EAAE,MAAM,EAAE,QAAQ;AAAA,MAC3B,WAAW;AAAA,QACT;AAAA,UACE,kBAAkB;AAAA,YAChB,kBAAkB,EAAE,KAAK,EAAE,KAAK;AAAA,YAChC,GAAI,SAAS,OAAO,EAAE,QAAQ,EAAE,WAAW,KAAK,EAAE,IAAI,CAAC;AAAA,UACzD;AAAA,QACF;AAAA,MACF;AAAA,MACA,GAAI,EAAE,iBAAiB,OACnB;AAAA,QACE,cAAc;AAAA,UACZ;AAAA,YACE,MAAM;AAAA,YACN,QAAQ;AAAA,YACR,eACE,mCACC,EAAE,aAAa,SAAS,KAAK,EAAE,aAAa,MAAM,KAAK,OACvD,EAAE,aAAa,UAAU,aAAa,EAAE,aAAa,OAAO,MAAM;AAAA,UACvE;AAAA,QACF;AAAA,MACF,IACA,CAAC;AAAA,MACL,GAAI,EAAE,gBAAgB,OAAO,EAAE,iBAAiB,EAAE,YAAY,IAAI,CAAC;AAAA,IACrE;AAAA,EACF,CAAC;AAED,QAAM,MAAM;AAAA,IACV,SAAS;AAAA,IACT,SAAS;AAAA,IACT,MAAM;AAAA,MACJ;AAAA,QACE,MAAM;AAAA,UACJ,QAAQ;AAAA,YACN,MAAM;AAAA,YACN,gBAAgB;AAAA,YAChB,SAAS;AAAA,YACT,OAAO,SAAS,IAAI,CAAC,QAAQ;AAAA,cAC3B;AAAA,cACA,kBAAkB,EAAE,MAAM,kBAAkB,EAAE,EAAE;AAAA,cAChD,SAAS;AAAA,YACX,EAAE;AAAA,UACJ;AAAA,QACF;AAAA,QACA,aAAa;AAAA,UACX;AAAA,YACE,qBAAqB;AAAA,YACrB,4BAA4B,OAAO,SAAS,IAAI,CAAC,OAAO;AAAA,cACtD,OAAO;AAAA,cACP,SAAS;AAAA,gBACP,OAAO,EAAE,eAAe,SAAY,GAAG,EAAE,UAAU,OAAO,MAAM,EAAE;AAAA,cACpE;AAAA,YACF,EAAE;AAAA,UACJ;AAAA,QACF;AAAA,QACA;AAAA,QACA,YAAY;AAAA,UACV,SAAS,OAAO;AAAA,UAChB,QAAQ,QAAQ;AAAA,UAChB,UAAU,QAAQ;AAAA,UAClB,OAAO,QAAQ;AAAA,UACf,YAAY,QAAQ;AAAA,QACtB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,GAAG,KAAK,UAAU,KAAK,MAAM,CAAC,CAAC;AAAA;AACxC;;;AC5HA,IAAM,MAAM,OAAO,aAAa,EAAE;AAElC,IAAM,OAAO;AAAA,EACX,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,OAAO;AAAA,EACP,KAAK;AAAA,EACL,MAAM;AACR;AAIA,SAAS,MAAM,MAAgB,MAAc,IAAqB;AAChE,SAAO,KAAK,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,QAAQ;AAClD;AAGA,SAAS,MAAM,GAAY,OAAwB;AACjD,MAAI,EAAE,iBAAiB,KAAM,QAAO,MAAM,KAAK,KAAK,WAAW,EAAE,QAAQ,KAAK,KAAK;AACnF,MAAI,EAAE,aAAa,QAAS,QAAO,MAAM,KAAK,KAAK,SAAS,KAAK;AACjE,MAAI,EAAE,aAAa,OAAQ,QAAO,MAAM,KAAK,QAAQ,QAAQ,KAAK;AAClE,SAAO,MAAM,KAAK,MAAM,QAAQ,KAAK;AACvC;AAEA,SAAS,OAAO,GAAW,MAAsB;AAC/C,SAAO,GAAG,OAAO,CAAC,CAAC,IAAI,IAAI,GAAG,MAAM,IAAI,KAAK,GAAG;AAClD;AAcO,SAAS,aAAa,QAAgB,SAAgC;AAC3E,QAAM,EAAE,MAAM,IAAI;AAClB,QAAM,QAAkB,CAAC;AAEzB,QAAM,iBAAiB,OAAO,iBAAiB,KAAK;AACpD,MAAI,cAA6B;AAEjC,aAAW,KAAK,OAAO,UAAU;AAC/B,QAAI,iBAAiB,EAAE,SAAS,aAAa;AAC3C,oBAAc,EAAE;AAChB,YAAM,KAAK,MAAM,KAAK,KAAK,GAAG,EAAE,IAAI,KAAK,KAAK,CAAC;AAC/C,YAAM,KAAK,EAAE;AAAA,IACf;AACA,UAAM,KAAK,GAAG,MAAM,GAAG,KAAK,CAAC,KAAK,MAAM,KAAK,MAAM,EAAE,SAAS,KAAK,CAAC,MAAM,EAAE,IAAI,GAAG;AACnF,UAAM,KAAK,OAAO,EAAE,OAAO,EAAE;AAC7B,QAAI,EAAE,iBAAiB,MAAM;AAC3B,YAAM,SAAS,EAAE,aAAa,UAAU;AACxC,YAAM,UAAU,EAAE,aAAa,UAAU,aAAa,EAAE,aAAa,OAAO,KAAK;AACjF,YAAM,KAAK,MAAM,KAAK,KAAK,kCAAkC,MAAM,GAAG,OAAO,IAAI,KAAK,CAAC;AAAA,IACzF;AACA,QAAI,EAAE,gBAAgB,MAAM;AAC1B,YAAM,KAAK,MAAM,KAAK,KAAK,iBAAiB,EAAE,WAAW,IAAI,KAAK,CAAC;AAAA,IACrE;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,MAAI,OAAO,SAAS,SAAS,GAAG;AAC9B,UAAM,KAAK,2EAA2E;AACtF,eAAW,KAAK,OAAO,UAAU;AAC/B,YAAM,QAAQ,EAAE,eAAe,SAAY,GAAG,EAAE,UAAU,OAAO;AACjE,YAAM,KAAK,MAAM,KAAK,KAAK,QAAQ,EAAE,MAAM,KAAK,KAAK,GAAG,EAAE,OAAO,IAAI,KAAK,CAAC;AAAA,IAC7E;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,QAAM,IAAI,UAAU,OAAO,QAAQ;AACnC,QAAM,IAAI,OAAO;AACjB,QAAM,UACJ,GAAG,OAAO,CAAC,CAAC,IAAI,MAAM,IAAI,eAAe,cAAc,MACtD,gBAAgB,OAAO,OAAO,OAAO,aAAa,CAAC,eAAe;AACrE,MAAI,EAAE,WAAW,KAAK,EAAE,aAAa,KAAK,EAAE,UAAU,KAAK,EAAE,eAAe,GAAG;AAC7E,UAAM,KAAK,MAAM,KAAK,OAAO,sBAAsB,OAAO,KAAK,KAAK,CAAC;AAAA,EACvE,OAAO;AACL,UAAM,QAAQ;AAAA,MACZ,MAAM,KAAK,KAAK,OAAO,EAAE,QAAQ,OAAO,GAAG,KAAK;AAAA,MAChD,MAAM,KAAK,QAAQ,OAAO,EAAE,UAAU,SAAS,GAAG,KAAK;AAAA,MACvD,OAAO,EAAE,OAAO,MAAM;AAAA,MACtB,GAAG,OAAO,EAAE,UAAU,CAAC;AAAA,IACzB;AACA,UAAM,KAAK,GAAG,MAAM,KAAK,IAAI,CAAC,WAAW,OAAO,GAAG;AAAA,EACrD;AAEA,SAAO,GAAG,MAAM,KAAK,IAAI,CAAC;AAAA;AAC5B;;;ACrGA,SAAS,WAAW,OAAuB;AACzC,SAAO,MAAM,WAAW,KAAK,KAAK,EAAE,WAAW,MAAM,KAAK,EAAE,WAAW,MAAM,KAAK;AACpF;AAMA,SAAS,eAAe,OAAuB;AAC7C,SAAO,WAAW,KAAK,EAAE,WAAW,KAAK,KAAK,EAAE,WAAW,KAAK,KAAK;AACvE;AAMA,SAAS,WAAW,GAA4C;AAC9D,MAAI,EAAE,iBAAiB,QAAQ,EAAE,aAAa,OAAQ,QAAO;AAC7D,SAAO,EAAE,aAAa,UAAU,UAAU;AAC5C;AAmBO,SAAS,kBACd,QACA,QACQ;AACR,QAAM,QAAkB,CAAC;AAEzB,aAAW,KAAK,OAAO,UAAU;AAC/B,UAAM,UAAU,WAAW,CAAC;AAC5B,UAAM,OAAO,OAAO,EAAE,MAAM,EAAE,OAAO;AACrC,UAAM,aAAa;AAAA,MACjB,QAAQ,eAAe,EAAE,IAAI,CAAC;AAAA,MAC9B,GAAI,SAAS,OAAO,CAAC,QAAQ,OAAO,IAAI,CAAC,EAAE,IAAI,CAAC;AAAA,MAChD,SAAS,eAAe,cAAc,EAAE,IAAI,KAAK,EAAE,OAAO,GAAG,CAAC;AAAA,IAChE,EAAE,KAAK,GAAG;AAEV,UAAM,SACJ,EAAE,iBAAiB,OACf,YAAY,EAAE,aAAa,SAAS,KAAK,EAAE,aAAa,MAAM,KAAK,EAAE,GAClE,EAAE,aAAa,UAAU,aAAa,EAAE,aAAa,OAAO,KAAK,EAAE,MACtE;AAEN,UAAM,KAAK,KAAK,OAAO,IAAI,UAAU,KAAK,WAAW,EAAE,UAAU,MAAM,CAAC,EAAE;AAAA,EAC5E;AAEA,SAAO,MAAM,SAAS,IAAI,GAAG,MAAM,KAAK,IAAI,CAAC;AAAA,IAAO;AACtD;;;ACnEA,SAAS,YAAAC,iBAAgB;AACzB,SAAS,QAAAC,aAAY;AAMd,IAAM,gBAAN,cAA4B,MAAM;AAAA,EACvC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAoCO,SAAS,sBACd,QACA,QAAQ,gBACoB;AAC5B,MAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,MAAM,QAAQ,MAAM,GAAG;AAC1E,UAAM,IAAI,cAAc,GAAG,KAAK,wBAAwB;AAAA,EAC1D;AACA,QAAM,OAAiB,OAAmC,cAAc;AACxE,MAAI,SAAS,OAAW,QAAO,CAAC;AAChC,MAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,MAAM,QAAQ,IAAI,GAAG;AACpE,UAAM,IAAI,cAAc,GAAG,KAAK,oDAAoD;AAAA,EACtF;AACA,SAAO,OAAO,QAAQ,IAAI,EAAE,IAAI,CAAC,CAAC,MAAM,IAAI,MAAM;AAChD,QAAI,OAAO,SAAS,UAAU;AAC5B,YAAM,IAAI,cAAc,GAAG,KAAK,6CAA6C,IAAI,IAAI;AAAA,IACvF;AACA,WAAO,EAAE,MAAM,KAAK;AAAA,EACtB,CAAC;AACH;AAUO,SAAS,iBAAiB,QAAiB,QAAQ,gBAAmC;AAC3F,SAAO,sBAAsB,QAAQ,KAAK,EAAE,IAAI,CAAC,UAAU,MAAM,IAAI;AACvE;AAWO,SAAS,wBAAwB,MAAc,OAA2C;AAC/F,QAAM,WAAW,KAAK,WAAW,CAAC,MAAM,QAAS,KAAK,MAAM,CAAC,IAAI;AACjE,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,QAAQ;AAAA,EAC9B,SAAS,KAAK;AACZ,UAAM,IAAI;AAAA,MACR,GAAG,KAAK,6BAAwB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IAClF;AAAA,EACF;AACA,SAAO,sBAAsB,QAAQ,KAAK;AAC5C;AAYA,eAAsB,eACpBC,OACA,gBAC0B;AAC1B,MAAI;AACJ,MAAI;AACF,WAAO,MAAMF,UAASE,OAAM,MAAM;AAGlC,QAAI,KAAK,WAAW,CAAC,MAAM,MAAQ,QAAO,KAAK,MAAM,CAAC;AAAA,EACxD,SAAS,KAAK;AACZ,UAAM,OAAQ,IAA8B;AAC5C,QAAI,SAAS,UAAU;AACrB,YAAM,IAAI,cAAc,kBAAkB,4BAA4BA,KAAI,GAAG;AAAA,IAC/E;AACA,UAAM,IAAI;AAAA,MACR,kBAAkBA,KAAI,WAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IAC9E;AAAA,EACF;AAEA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,IAAI;AAAA,EAC1B,SAAS,KAAK;AACZ,UAAM,IAAI;AAAA,MACR,GAAGA,KAAI,6BAAwB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IACjF;AAAA,EACF;AAEA,QAAM,UAAU,sBAAsB,QAAQA,KAAI;AAClD,SAAO,EAAE,MAAAA,OAAM,MAAM,cAAc,QAAQ,IAAI,CAAC,UAAU,MAAM,IAAI,GAAG,QAAQ;AACjF;AAUA,eAAsB,gBAAgB,KAAuC;AAC3E,SAAO;AAAA,IACLD,MAAK,KAAK,cAAc;AAAA,IACxB,4BAA4B,GAAG;AAAA,EAEjC;AACF;AAeO,SAAS,mBAAmB,MAAc,MAA6B;AAC5E,MAAI,OAAO;AACX,MAAI,QAAQ;AACZ,MAAI,iBAAiB;AAErB,MAAI,IAAI;AACR,SAAO,IAAI,KAAK,QAAQ;AACtB,UAAM,KAAK,KAAK,CAAC;AAEjB,QAAI,OAAO,MAAM;AACf;AACA;AACA;AAAA,IACF;AAEA,QAAI,OAAO,KAAK;AAEd,YAAM,aAAa;AACnB,UAAI,QAAQ;AACZ;AACA,aAAO,IAAI,KAAK,QAAQ;AACtB,cAAM,IAAI,KAAK,CAAC;AAChB,YAAI,MAAM,MAAM;AACd,mBAAS,KAAK,MAAM,GAAG,IAAI,CAAC;AAC5B,eAAK;AACL;AAAA,QACF;AACA,YAAI,MAAM,KAAM;AAChB,YAAI,MAAM,KAAK;AACb;AACA;AAAA,QACF;AACA,iBAAS;AACT;AAAA,MACF;AAEA,UAAI,IAAI;AACR,aAAO,IAAI,KAAK,WAAW,KAAK,CAAC,MAAM,OAAO,KAAK,CAAC,MAAM,OAAQ,KAAK,CAAC,MAAM,QAAQ,KAAK,CAAC,MAAM,MAAO;AACzG,YAAM,QAAQ,KAAK,CAAC,MAAM;AAC1B,UAAI,OAAO;AACT,YAAI,UAAU,KAAK,UAAU,gBAAgB;AAG3C,cAAI,IAAI,IAAI;AACZ,iBAAO,IAAI,KAAK,WAAW,KAAK,CAAC,MAAM,OAAO,KAAK,CAAC,MAAM,OAAQ,KAAK,CAAC,MAAM,QAAQ,KAAK,CAAC,MAAM,MAAO;AACzG,cAAI,KAAK,CAAC,MAAM,IAAK,kBAAiB;AAAA,QACxC,WAAW,kBAAkB,UAAU,KAAK,UAAU,MAAM;AAC1D,iBAAO;AAAA,QACT;AAAA,MACF;AACA;AAAA,IACF;AAEA,QAAI,OAAO,OAAO,OAAO,KAAK;AAC5B;AAAA,IACF,WAAW,OAAO,OAAO,OAAO,KAAK;AACnC;AACA,UAAI,kBAAkB,SAAS,EAAG,kBAAiB;AAAA,IACrD;AACA;AAAA,EACF;AAEA,SAAO;AACT;;;AClOO,SAAS,iBACd,MACA,SACmB;AACnB,MAAI,SAAS,KAAM,QAAO,QAAQ,IAAI,CAAC,UAAU,MAAM,IAAI;AAE3D,QAAM,YAAY,IAAI,IAAI,KAAK,IAAI,CAAC,UAAU,CAAC,MAAM,MAAM,MAAM,IAAI,CAAC,CAAC;AACvE,SAAO,QACJ,OAAO,CAAC,UAAU,UAAU,IAAI,MAAM,IAAI,MAAM,MAAM,IAAI,EAC1D,IAAI,CAAC,UAAU,MAAM,IAAI;AAC9B;;;ACpBA,SAAS,gBAAgB;AAMlB,IAAM,WAAN,cAAuB,MAAM;AAAA,EAClC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAqBO,SAAS,kBAA6B;AAC3C,SAAO,CAAC,MAAM,QACZ,IAAI,QAAQ,CAACE,UAAS,WAAW;AAC/B;AAAA,MACE;AAAA,MACA,CAAC,GAAG,IAAI;AAAA,MACR,EAAE,KAAK,UAAU,QAAQ,aAAa,MAAM,WAAW,KAAK,OAAO,KAAK;AAAA,MACxE,CAAC,KAAK,QAAQ,WAAW;AACvB,YAAI,OAAQ,IAA8B,SAAS,UAAU;AAC3D;AAAA,YACE,IAAI,SAAS,sEAAiE;AAAA,UAChF;AACA;AAAA,QACF;AAEA,cAAM,OAAO,MAAQ,IAAmC,QAAQ,IAAK;AACrE,QAAAA,SAAQ,EAAE,MAAM,OAAO,SAAS,WAAW,OAAO,GAAG,QAAQ,OAAO,CAAC;AAAA,MACvE;AAAA,IACF;AAAA,EACF,CAAC;AACL;AAkBA,eAAsB,kBACpB,KACA,KACA,SACiB;AACjB,QAAM,SAAS,MAAM,IAAI,CAAC,cAAc,QAAQ,OAAO,GAAG,GAAG;AAC7D,MAAI,OAAO,SAAS,GAAG;AACrB,UAAM,MAAM,OAAO,OAAO,KAAK;AAC/B,QAAI,QAAQ,GAAI,QAAO;AAAA,EACzB;AAEA,QAAM,SAAS,OAAO;AACtB,MAAI,wBAAwB,KAAK,MAAM,GAAG;AACxC,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACA,MAAI,kFAAkF,KAAK,MAAM,GAAG;AAClG,UAAM,IAAI;AAAA,MACR,aAAa,OAAO;AAAA,IAEtB;AAAA,EACF;AACA,MAAI,OAAO,KAAK,MAAM,IAAI;AAExB,UAAM,IAAI;AAAA,MACR,mCAAmC,OAAO;AAAA,IAE5C;AAAA,EACF;AACA,QAAM,IAAI,SAAS,uBAAuB,OAAO,kBAAa,OAAO,KAAK,CAAC,EAAE;AAC/E;AAkBA,eAAsB,iBACpB,KACA,KACA,QACA,SACwB;AACxB,QAAM,YAAY,QAAQ,WAAW,MAAM,GAAG;AAC9C,QAAM,SAAS,MAAM,IAAI,CAAC,QAAQ,GAAG,MAAM,MAAM,SAAS,EAAE,GAAG,GAAG;AAElE,MAAI,OAAO,SAAS,GAAG;AACrB,QAAI,OAAO,OAAO;AAClB,QAAI,KAAK,WAAW,CAAC,MAAM,MAAQ,QAAO,KAAK,MAAM,CAAC;AACtD,WAAO;AAAA,EACT;AAEA,MAAI,mGAAmG,KAAK,OAAO,MAAM,GAAG;AAC1H,WAAO;AAAA,EACT;AACA,QAAM,IAAI,SAAS,YAAY,MAAM,MAAM,SAAS,kBAAa,OAAO,OAAO,KAAK,CAAC,EAAE;AACzF;;;ACrIA,SAAS,SAAS,YAAAC,WAAU,YAAY;AACxC,SAAS,QAAAC,OAAM,UAAU,WAAW;AAEpC,SAAS,SAAAC,cAAa;AAMf,IAAM,iBAAN,cAA6B,MAAM;AAAA,EACxC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAcA,SAAS,gBAAgB,SAAyB;AAChD,QAAM,UAAU,QAAQ,QAAQ,sBAAsB,MAAM,EAAE,WAAW,KAAK,WAAW;AACzF,SAAO,IAAI,OAAO,IAAI,OAAO,GAAG;AAClC;AAEA,eAAe,YAAYC,OAAgC;AACzD,MAAI;AACF,YAAQ,MAAM,KAAKA,KAAI,GAAG,YAAY;AAAA,EACxC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,eAAe,YAAY,KAAyC;AAClE,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAAA,EACtD,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACA,SAAO,QACJ,OAAO,CAAC,MAAM,EAAE,YAAY,KAAK,EAAE,SAAS,kBAAkB,CAAC,EAAE,KAAK,WAAW,GAAG,CAAC,EACrF,IAAI,CAAC,MAAM,EAAE,IAAI;AACtB;AAGA,eAAe,SAAS,KAAyC;AAC/D,QAAM,MAAgB,CAAC,GAAG;AAC1B,aAAW,QAAQ,MAAM,YAAY,GAAG,GAAG;AACzC,QAAI,KAAK,GAAI,MAAM,SAASF,MAAK,KAAK,IAAI,CAAC,CAAE;AAAA,EAC/C;AACA,SAAO;AACT;AAGA,eAAe,cAAc,SAAiB,SAA6C;AAEzF,QAAM,WAAW,QAAQ,WAAW,MAAM,GAAG,EAAE,QAAQ,QAAQ,EAAE,EAAE,MAAM,GAAG,EAAE,OAAO,CAAC,MAAM,MAAM,MAAM,MAAM,GAAG;AAEjH,MAAI,OAA0B,CAAC,OAAO;AACtC,aAAW,CAAC,OAAO,OAAO,KAAK,SAAS,QAAQ,GAAG;AACjD,QAAI,YAAY,MAAM;AAGpB,YAAM,OAAO,SAAS,MAAM,QAAQ,CAAC,EAAE,KAAK,GAAG;AAC/C,YAAM,SAAS,MAAM,QAAQ,IAAI,KAAK,IAAI,CAAC,MAAM,SAAS,CAAC,CAAC,CAAC,GAAG,KAAK;AACrE,UAAI,SAAS,GAAI,QAAO;AACxB,cAAQ,MAAM,QAAQ,IAAI,MAAM,IAAI,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC,CAAC,GAAG,KAAK;AAAA,IAC5E;AACA,QAAI,QAAQ,SAAS,GAAG,GAAG;AACzB,YAAM,KAAK,gBAAgB,OAAO;AAClC,YAAM,OAAiB,CAAC;AACxB,iBAAW,OAAO,MAAM;AACtB,mBAAW,QAAQ,MAAM,YAAY,GAAG,GAAG;AACzC,cAAI,GAAG,KAAK,IAAI,EAAG,MAAK,KAAKA,MAAK,KAAK,IAAI,CAAC;AAAA,QAC9C;AAAA,MACF;AACA,aAAO;AAAA,IACT,OAAO;AACL,YAAM,OAAiB,CAAC;AACxB,iBAAW,OAAO,MAAM;AACtB,cAAM,YAAYA,MAAK,KAAK,OAAO;AACnC,YAAI,MAAM,YAAY,SAAS,EAAG,MAAK,KAAK,SAAS;AAAA,MACvD;AACA,aAAO;AAAA,IACT;AACA,QAAI,KAAK,WAAW,EAAG,QAAO,CAAC;AAAA,EACjC;AACA,SAAO;AACT;AAGA,SAAS,SAAS,SAAiB,KAAqB;AACtD,SAAO,SAAS,SAAS,GAAG,EAAE,MAAM,GAAG,EAAE,KAAK,GAAG;AACnD;AAaA,eAAsB,wBACpB,SACA,UAC4B;AAC5B,QAAM,WAAW,oBAAI,IAAoB;AACzC,QAAM,aAAuB,CAAC;AAE9B,aAAW,WAAW,UAAU;AAC9B,QAAI,QAAQ,WAAW,GAAG,GAAG;AAC3B,iBAAW,KAAK,QAAQ,MAAM,CAAC,CAAC;AAChC;AAAA,IACF;AACA,eAAW,OAAO,MAAM,cAAc,SAAS,OAAO,GAAG;AACvD,YAAM,MAAM,SAAS,SAAS,GAAG;AACjC,UAAI,QAAQ,GAAI,UAAS,IAAI,KAAK,GAAG;AAAA,IACvC;AAAA,EACF;AAEA,aAAW,aAAa,YAAY;AAClC,UAAM,WAAW,IAAI;AAAA,OAClB,MAAM,cAAc,SAAS,SAAS,GAAG,IAAI,CAAC,QAAQ,SAAS,SAAS,GAAG,CAAC;AAAA,IAC/E;AACA,eAAW,OAAO,CAAC,GAAG,SAAS,KAAK,CAAC,GAAG;AACtC,UAAI,SAAS,IAAI,GAAG,EAAG,UAAS,OAAO,GAAG;AAAA,IAC5C;AAAA,EACF;AAEA,SAAO,CAAC,GAAG,SAAS,QAAQ,CAAC,EAC1B,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAO,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAE,EAC/C,IAAI,CAAC,CAAC,EAAE,GAAG,MAAM,GAAG;AACzB;AAGA,eAAe,0BAA0B,SAAoD;AAC3F,MAAI;AACJ,MAAI;AACF,WAAO,MAAMD,UAASC,MAAK,SAAS,qBAAqB,GAAG,MAAM;AAAA,EACpE,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI;AACJ,MAAI;AACF,aAASC,OAAM,IAAI;AAAA,EACrB,SAAS,KAAK;AACZ,UAAM,IAAI;AAAA,MACR,gDAA2C,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IAC7F;AAAA,EACF;AACA,MAAI,OAAO,WAAW,YAAY,WAAW,KAAM,QAAO;AAC1D,QAAM,WAAqB,OAAmC,UAAU;AACxE,MAAI,aAAa,OAAW,QAAO;AACnC,MAAI,CAAC,MAAM,QAAQ,QAAQ,KAAK,CAAC,SAAS,MAAM,CAAC,MAAmB,OAAO,MAAM,QAAQ,GAAG;AAC1F,UAAM,IAAI,eAAe,2EAA2E;AAAA,EACtG;AACA,SAAO;AACT;AAGA,SAAS,8BAA8B,QAA2C;AAChF,MAAI,OAAO,WAAW,YAAY,WAAW,KAAM,QAAO;AAC1D,MAAI,QAAkB,OAAmC,YAAY;AACrE,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK,GAAG;AACxE,YAAS,MAAkC,UAAU;AACrD,QAAI,UAAU,OAAW,QAAO;AAAA,EAClC;AACA,MAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,CAAC,MAAM,MAAM,CAAC,MAAmB,OAAO,MAAM,QAAQ,GAAG;AACpF,UAAM,IAAI,eAAe,sEAAsE;AAAA,EACjG;AACA,SAAO;AACT;AAeA,eAAsB,mBACpB,SACA,oBACkC;AAClC,QAAM,WACH,MAAM,0BAA0B,OAAO,KAAM,8BAA8B,kBAAkB;AAChG,MAAI,aAAa,MAAM;AACrB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAyB,CAAC,EAAE,KAAK,SAAS,iBAAiB,eAAe,CAAC;AACjF,aAAW,OAAO,MAAM,wBAAwB,SAAS,QAAQ,GAAG;AAClE,QAAI;AACF,YAAM,KAAKD,MAAK,KAAK,cAAc,CAAC;AAAA,IACtC,QAAQ;AACN;AAAA,IACF;AACA,WAAO,KAAK,EAAE,KAAK,iBAAiB,GAAG,SAAS,SAAS,GAAG,CAAC,gBAAgB,CAAC;AAAA,EAChF;AACA,SAAO;AACT;","names":["parseGithubUrl","parseGithubUrl","path","readFile","join","path","resolve","readFile","join","parse","path"]}
package/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "react-native-doctor-ci",
3
+ "version": "0.1.0",
4
+ "description": "Policy-as-code CI gate for React Native dependency health: fail PRs that add abandoned, non-New-Architecture, or npm-deprecated dependencies.",
5
+ "keywords": [
6
+ "react-native",
7
+ "ci",
8
+ "dependencies",
9
+ "new-architecture",
10
+ "policy",
11
+ "lint",
12
+ "github-actions",
13
+ "sarif"
14
+ ],
15
+ "license": "MIT",
16
+ "author": "Amrith Vengalath",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/AmrithVengalath/react-native-doctor-ci.git"
20
+ },
21
+ "homepage": "https://github.com/AmrithVengalath/react-native-doctor-ci#readme",
22
+ "bugs": "https://github.com/AmrithVengalath/react-native-doctor-ci/issues",
23
+ "type": "module",
24
+ "bin": {
25
+ "rn-doctor": "./dist/cli.js"
26
+ },
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "import": "./dist/index.js",
31
+ "require": "./dist/index.cjs"
32
+ }
33
+ },
34
+ "main": "./dist/index.cjs",
35
+ "module": "./dist/index.js",
36
+ "types": "./dist/index.d.ts",
37
+ "files": [
38
+ "dist"
39
+ ],
40
+ "engines": {
41
+ "node": ">=20"
42
+ },
43
+ "publishConfig": {
44
+ "access": "public",
45
+ "provenance": true
46
+ },
47
+ "devDependencies": {
48
+ "@eslint/js": "^10.0.1",
49
+ "@release-it/conventional-changelog": "^11.0.1",
50
+ "@types/node": "^20.19.0",
51
+ "@vitest/coverage-v8": "^4.1.10",
52
+ "ajv": "^8.20.0",
53
+ "eslint": "^10.7.0",
54
+ "jiti": "^2.7.0",
55
+ "msw": "^2.15.0",
56
+ "release-it": "^20.2.1",
57
+ "tsup": "^8.5.1",
58
+ "typescript": "6.0.3",
59
+ "typescript-eslint": "^8.63.0",
60
+ "vitest": "^4.1.10"
61
+ },
62
+ "dependencies": {
63
+ "yaml": "^2.9.0"
64
+ },
65
+ "scripts": {
66
+ "typecheck": "tsc --noEmit",
67
+ "lint": "eslint .",
68
+ "lint:fix": "eslint . --fix",
69
+ "test": "vitest run",
70
+ "test:watch": "vitest",
71
+ "test:cov": "vitest run --coverage",
72
+ "build": "tsup",
73
+ "dev": "tsup --watch",
74
+ "release": "release-it"
75
+ }
76
+ }