@open-mercato/cli 0.10.1-develop.7803.1.e99e862b4e → 0.10.1-develop.7807.1.9b2b8953e9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/dist/agentic/guides/module-facts.json +162 -162
  2. package/dist/agentic/guides/module-facts.v2.json +162 -162
  3. package/dist/agentic/guides/modules/agent_orchestrator/index.md +1 -1
  4. package/dist/agentic/guides/modules/ai_assistant/index.md +1 -1
  5. package/dist/agentic/guides/modules/api_docs/index.md +1 -1
  6. package/dist/agentic/guides/modules/api_keys/index.md +1 -1
  7. package/dist/agentic/guides/modules/attachments/index.md +1 -1
  8. package/dist/agentic/guides/modules/audit_logs/index.md +1 -1
  9. package/dist/agentic/guides/modules/auth/index.md +1 -1
  10. package/dist/agentic/guides/modules/availability/index.md +1 -1
  11. package/dist/agentic/guides/modules/business_rules/index.md +1 -1
  12. package/dist/agentic/guides/modules/carrier_inpost/index.md +1 -1
  13. package/dist/agentic/guides/modules/cart/index.md +1 -1
  14. package/dist/agentic/guides/modules/catalog/index.md +1 -1
  15. package/dist/agentic/guides/modules/channel_apns/index.md +1 -1
  16. package/dist/agentic/guides/modules/channel_discord/index.md +1 -1
  17. package/dist/agentic/guides/modules/channel_expo/index.md +1 -1
  18. package/dist/agentic/guides/modules/channel_fcm/index.md +1 -1
  19. package/dist/agentic/guides/modules/channel_gmail/index.md +1 -1
  20. package/dist/agentic/guides/modules/channel_imap/index.md +1 -1
  21. package/dist/agentic/guides/modules/channel_resend/index.md +1 -1
  22. package/dist/agentic/guides/modules/channel_ses/index.md +1 -1
  23. package/dist/agentic/guides/modules/channel_smtp/index.md +1 -1
  24. package/dist/agentic/guides/modules/checkout/index.md +1 -1
  25. package/dist/agentic/guides/modules/communication_channels/index.md +1 -1
  26. package/dist/agentic/guides/modules/configs/index.md +1 -1
  27. package/dist/agentic/guides/modules/content/index.md +1 -1
  28. package/dist/agentic/guides/modules/currencies/index.md +1 -1
  29. package/dist/agentic/guides/modules/customer_accounts/index.md +1 -1
  30. package/dist/agentic/guides/modules/customer_groups/index.md +1 -1
  31. package/dist/agentic/guides/modules/customers/index.md +1 -1
  32. package/dist/agentic/guides/modules/dashboards/index.md +1 -1
  33. package/dist/agentic/guides/modules/data_sync/index.md +1 -1
  34. package/dist/agentic/guides/modules/design_system/index.md +1 -1
  35. package/dist/agentic/guides/modules/devices/index.md +1 -1
  36. package/dist/agentic/guides/modules/dictionaries/index.md +1 -1
  37. package/dist/agentic/guides/modules/directory/index.md +1 -1
  38. package/dist/agentic/guides/modules/documents/index.md +1 -1
  39. package/dist/agentic/guides/modules/ecommerce/index.md +1 -1
  40. package/dist/agentic/guides/modules/entities/index.md +1 -1
  41. package/dist/agentic/guides/modules/eudr/index.md +1 -1
  42. package/dist/agentic/guides/modules/events/index.md +1 -1
  43. package/dist/agentic/guides/modules/feature_toggles/index.md +1 -1
  44. package/dist/agentic/guides/modules/forms/index.md +1 -1
  45. package/dist/agentic/guides/modules/gateway_autopay/index.md +1 -1
  46. package/dist/agentic/guides/modules/gateway_stripe/index.md +1 -1
  47. package/dist/agentic/guides/modules/gateway_tpay/index.md +1 -1
  48. package/dist/agentic/guides/modules/generators/index.md +1 -1
  49. package/dist/agentic/guides/modules/inbox_ops/index.md +1 -1
  50. package/dist/agentic/guides/modules/integrations/index.md +1 -1
  51. package/dist/agentic/guides/modules/messages/index.md +1 -1
  52. package/dist/agentic/guides/modules/notifications/index.md +1 -1
  53. package/dist/agentic/guides/modules/onboarding/index.md +1 -1
  54. package/dist/agentic/guides/modules/payment_gateways/index.md +1 -1
  55. package/dist/agentic/guides/modules/perspectives/index.md +1 -1
  56. package/dist/agentic/guides/modules/phone_calls/index.md +1 -1
  57. package/dist/agentic/guides/modules/planner/index.md +1 -1
  58. package/dist/agentic/guides/modules/portal/index.md +1 -1
  59. package/dist/agentic/guides/modules/progress/index.md +1 -1
  60. package/dist/agentic/guides/modules/promotions/index.md +1 -1
  61. package/dist/agentic/guides/modules/push_notifications/index.md +1 -1
  62. package/dist/agentic/guides/modules/query_index/index.md +1 -1
  63. package/dist/agentic/guides/modules/record_locks/index.md +1 -1
  64. package/dist/agentic/guides/modules/resources/index.md +1 -1
  65. package/dist/agentic/guides/modules/sales/index.md +1 -1
  66. package/dist/agentic/guides/modules/scheduler/index.md +1 -1
  67. package/dist/agentic/guides/modules/search/index.md +1 -1
  68. package/dist/agentic/guides/modules/security/index.md +1 -1
  69. package/dist/agentic/guides/modules/seeds/index.md +1 -1
  70. package/dist/agentic/guides/modules/shipping_carriers/index.md +1 -1
  71. package/dist/agentic/guides/modules/sso/index.md +1 -1
  72. package/dist/agentic/guides/modules/staff/index.md +1 -1
  73. package/dist/agentic/guides/modules/storage_s3/index.md +1 -1
  74. package/dist/agentic/guides/modules/sync_akeneo/index.md +1 -1
  75. package/dist/agentic/guides/modules/sync_excel/index.md +1 -1
  76. package/dist/agentic/guides/modules/system_status_overlays/index.md +1 -1
  77. package/dist/agentic/guides/modules/telemetry/index.md +1 -1
  78. package/dist/agentic/guides/modules/tillio/index.md +1 -1
  79. package/dist/agentic/guides/modules/translations/index.md +1 -1
  80. package/dist/agentic/guides/modules/warranty_claims/index.md +1 -1
  81. package/dist/agentic/guides/modules/webhooks/index.md +1 -1
  82. package/dist/agentic/guides/modules/wms/index.md +1 -1
  83. package/dist/agentic/guides/modules/workflows/index.md +1 -1
  84. package/dist/agentic/guides/reference-module-facts.json +1 -1
  85. package/dist/agentic/guides/upstream/manifest.json +1 -1
  86. package/dist/lib/module-search-index.js +7 -3
  87. package/dist/lib/module-search-index.js.map +2 -2
  88. package/dist/lib/module-search.js +62 -3
  89. package/dist/lib/module-search.js.map +2 -2
  90. package/package.json +7 -7
  91. package/src/lib/__tests__/module-search.test.ts +111 -0
  92. package/src/lib/module-search-index.ts +7 -3
  93. package/src/lib/module-search.ts +65 -4
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/module-search.ts"],
4
- "sourcesContent": ["import fs from 'node:fs'\nimport path from 'node:path'\nimport type { ModuleEntry } from './modules-config'\nimport { createResolver } from './resolver'\nimport {\n builtInEnableCommand,\n builtInPresetText,\n builtInWarnings,\n cardUrl,\n CORE_PACKAGE,\n createCatalogSearcher,\n ENTERPRISE_ENABLE_STEP,\n entryWarnings,\n installFields,\n installVersion,\n latestLine,\n newAppInstallText,\n peerLookup,\n rankModules,\n receiptSourceOf,\n resolveLine,\n resolveRef,\n SEARCH_MAX_LIMIT,\n sharedModuleIdWarning,\n statusOn,\n stripVersion,\n toModuleCard,\n unknownModuleMessage,\n type AgentPolicy,\n type BuiltInCard,\n type BuiltInIndex,\n type BuiltInModule,\n type CatalogEntry,\n type CatalogIndex,\n type CatalogSearcher,\n type ModuleCard,\n type ReceiptSource,\n} from './module-search-index'\nimport { parseModuleInfoArgs, parseModuleSearchArgs } from './module-search-args'\n\nexport const DEFAULT_MODULES_INDEX_URL = 'https://modules.openmercato.com/modules.json'\nexport const MODULES_SITE_URL = 'https://modules.openmercato.com'\nexport const MODULE_INDEX_TIMEOUT_MS = 4000\nexport const MODULE_INDEX_TTL_MS = 10 * 60 * 1000\nexport const MODULE_INDEX_CACHE_FILE = path.join('.mercato', 'cache', 'modules-index.json')\nexport const MODULE_INDEX_UNAVAILABLE_EXIT_CODE = 2\nexport const MODULE_INDEX_UNAVAILABLE_MESSAGE =\n 'Module index unavailable. Check your connection or set OM_MODULES_INDEX_URL.'\n\nexport type FetchResponseLike = {\n status: number\n ok: boolean\n headers: { get(name: string): string | null }\n json(): Promise<unknown>\n}\n\nexport type FetchLike = (\n input: string,\n init?: { headers?: Record<string, string>; signal?: AbortSignal },\n) => Promise<FetchResponseLike>\n\nexport type LoadedModuleIndex = {\n index: CatalogIndex\n builtIn: BuiltInIndex | null\n /** Why the built-in module list is missing (`builtIn` is null), or null when it loaded. */\n builtInUnavailable: string | null\n source: 'network' | 'cache' | 'stale-cache'\n fetchedAt: string\n searcher: CatalogSearcher\n}\n\nexport type LoadIndexOptions = {\n appRoot: string\n url?: string\n builtInUrl?: string\n timeoutMs?: number\n ttlMs?: number\n fetchImpl?: FetchLike\n now?: () => number\n}\n\ntype CacheFile = {\n cacheVersion: 1\n url: string\n builtInUrl: string\n fetchedAt: string\n etag: string | null\n builtInEtag: string | null\n index: CatalogIndex\n builtIn: BuiltInIndex | null\n}\n\nexport class ModuleIndexUnavailableError extends Error {\n readonly detail: string\n\n constructor(detail: string) {\n super(`${MODULE_INDEX_UNAVAILABLE_MESSAGE} (${detail})`)\n this.name = 'ModuleIndexUnavailableError'\n this.detail = detail\n }\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\nconst isString = (value: unknown): value is string => typeof value === 'string'\nconst isStringArray = (value: unknown): value is string[] => Array.isArray(value) && value.every(isString)\nconst isNullableString = (value: unknown): value is string | null => value === null || isString(value)\nconst isArrayOf = (value: unknown, item: (value: unknown) => boolean): boolean => Array.isArray(value) && value.every(item)\nconst isRecordOf = (value: unknown, item: (value: unknown) => boolean): boolean =>\n isRecord(value) && Object.values(value).every(item)\nconst isAbsentOr = (value: unknown, check: (value: unknown) => boolean): boolean => value === undefined || check(value)\n\nconst ANATOMY_LISTS = ['aclFeatures', 'eventsEmitted', 'eventsConsumed', 'env', 'hosts', 'tables', 'publicRoutes'] as const\n\n// The anatomy lists search indexes and `module info` prints; a module without anatomy has null.\nfunction isAnatomy(value: unknown): boolean {\n if (value === null || value === undefined) return true\n if (!isRecord(value)) return false\n const lists = value.lists\n if (lists === undefined) return true\n return (\n isRecord(lists) &&\n ANATOMY_LISTS.every((key) => isAbsentOr(lists[key], isStringArray)) &&\n isAbsentOr(lists.credentials, (credentials) =>\n isArrayOf(credentials, (credential) => isRecord(credential) && isString(credential.key)),\n )\n )\n}\n\nfunction isAgentPolicy(value: unknown): boolean {\n return (\n isRecord(value) &&\n isString(value.decision) &&\n isArrayOf(value.reasons, (reason) => isRecord(reason) && isString(reason.code) && isString(reason.text))\n )\n}\n\nfunction isBuiltFor(value: unknown): boolean {\n return (\n isRecord(value) &&\n isString(value.kind) &&\n isNullableString(value.range) &&\n isStringArray(value.lines) &&\n isAbsentOr(value.ranges, (ranges) => isRecordOf(ranges, isString))\n )\n}\n\nfunction isCompat(value: unknown): boolean {\n return (\n isRecord(value) &&\n isRecordOf(\n value.lines,\n (cell) =>\n isRecord(cell) &&\n isString(cell.status) &&\n isAbsentOr(cell.at, isNullableString) &&\n isAbsentOr(cell.moduleVersion, isNullableString),\n ) &&\n isStringArray(value.verifiedOn) &&\n isAbsentOr(value.builtFor, isBuiltFor) &&\n isAbsentOr(value.inPresets, isStringArray) &&\n isAbsentOr(value.inEmptyPreset, isStringArray) &&\n isAbsentOr(value.ships, (ships) => isRecordOf(ships, isString)) &&\n isAbsentOr(value.isolation, (isolation) =>\n isRecordOf(isolation, (cell) => isRecord(cell) && isString(cell.status) && isNullableString(cell.moduleVersion)),\n )\n )\n}\n\nfunction isSignals(value: unknown): boolean {\n if (value === null || value === undefined) return true\n return (\n isRecord(value) &&\n isAbsentOr(value.supplyChain, (item) => isRecord(item) && isString(item.status) && isStringArray(item.reasons)) &&\n isAbsentOr(value.hygiene, (item) => isRecord(item) && isString(item.status) && isStringArray(item.failing)) &&\n isAbsentOr(value.anatomy, isString)\n )\n}\n\n// Every field search, `module info` and their output read, nested ones too: an entry that passes is never the reason\n// a command throws, so a malformed index is refused before it can replace a working cached copy.\nfunction isCatalogEntry(value: unknown): value is CatalogEntry {\n if (!isRecord(value)) return false\n const version = value.version\n const install = value.install\n const links = value.links\n const license = value.license\n return (\n isString(value.locator) &&\n isString(value.path) &&\n (value.npm === null || isString(value.npm)) &&\n isString(value.title) &&\n isString(value.summary) &&\n isString(value.whenToUse) &&\n isString(value.type) &&\n isString(value.kind) &&\n isString(value.category) &&\n isStringArray(value.tags) &&\n isArrayOf(value.providers, (provider) => isRecord(provider) && isString(provider.name) && isString(provider.label)) &&\n isStringArray(value.moduleIds) &&\n isAbsentOr(value.requires, (requires) =>\n isArrayOf(\n requires,\n (requirement) => isRecord(requirement) && isString(requirement.raw) && isAbsentOr(requirement.resolvesTo, isNullableString),\n ),\n ) &&\n isStringArray(value.renamedFrom) &&\n isString(value.status) &&\n typeof value.listed === 'boolean' &&\n typeof value.featured === 'boolean' &&\n isNullableString(value.tier) &&\n isRecord(license) &&\n isNullableString(license.spdx) &&\n isString(license.kind) &&\n isRecord(version) &&\n isNullableString(version.latest) &&\n isNullableString(version.preview) &&\n isNullableString(version.installableFrom) &&\n isNullableString(version.gitRef) &&\n isRecordOf(version.recommended, isNullableString) &&\n isCompat(value.compat) &&\n isAnatomy(value.anatomy) &&\n isSignals(value.signals) &&\n isRecordOf(value.agentPolicy, isAgentPolicy) &&\n isRecord(install) &&\n isString(install.cli) &&\n isAbsentOr(install.moduleFlag, isNullableString) &&\n isRecord(links) &&\n isString(links.page) &&\n isString(links.md)\n )\n}\n\nexport function parseCatalogIndex(value: unknown): CatalogIndex | null {\n if (!isRecord(value) || value.schemaVersion !== 1) return null\n if (!isString(value.generatedAt) || !isString(value.dataVersion) || !isString(value.site)) return null\n if (!Array.isArray(value.lines) || !Array.isArray(value.entries)) return null\n if (!isAbsentOr(value.dataAsOf, isRecord)) return null\n if (!value.lines.every((line) => isRecord(line) && isString(line.key) && isString(line.version) && isString(line.source))) {\n return null\n }\n if (!value.entries.every(isCatalogEntry)) return null\n const lines = value.lines.filter(\n (line): line is CatalogIndex['lines'][number] =>\n line.source === 'latest' || line.source === 'previous' || line.source === 'develop',\n )\n return { ...(value as CatalogIndex), lines }\n}\n\nexport function parseBuiltInIndex(value: unknown): BuiltInIndex | null {\n if (!isRecord(value) || value.schemaVersion !== 1 || !isString(value.coreVersion) || !Array.isArray(value.modules)) {\n return null\n }\n const valid = value.modules.every(\n (coreModule) =>\n isRecord(coreModule) &&\n isString(coreModule.id) &&\n isString(coreModule.title) &&\n isString(coreModule.description) &&\n isString(coreModule.category) &&\n isString(coreModule.install) &&\n isString(coreModule.page) &&\n isAbsentOr(coreModule.requires, isStringArray) &&\n isAbsentOr(coreModule.inPresets, isStringArray) &&\n isAbsentOr(coreModule.inEmptyPreset, isStringArray) &&\n isAnatomy(coreModule.anatomy),\n )\n return valid ? (value as BuiltInIndex) : null\n}\n\nfunction parseCacheFile(value: unknown): CacheFile | null {\n if (!isRecord(value) || value.cacheVersion !== 1) return null\n if (!isString(value.url) || !isString(value.builtInUrl) || !isString(value.fetchedAt)) return null\n const index = parseCatalogIndex(value.index)\n if (!index) return null\n const builtIn = value.builtIn === null ? null : parseBuiltInIndex(value.builtIn)\n return {\n cacheVersion: 1,\n url: value.url,\n builtInUrl: value.builtInUrl,\n fetchedAt: value.fetchedAt,\n etag: isString(value.etag) ? value.etag : null,\n builtInEtag: isString(value.builtInEtag) ? value.builtInEtag : null,\n index,\n builtIn,\n }\n}\n\nexport function defaultBuiltInUrl(indexUrl: string): string {\n const url = new URL(indexUrl)\n url.search = ''\n url.hash = ''\n url.pathname = url.pathname.replace(/[^/]*$/, 'built-in.json')\n return url.toString()\n}\n\nfunction readCache(file: string): CacheFile | null {\n try {\n return parseCacheFile(JSON.parse(fs.readFileSync(file, 'utf8')))\n } catch {\n return null\n }\n}\n\nfunction writeCache(file: string, cache: CacheFile): void {\n try {\n fs.mkdirSync(path.dirname(file), { recursive: true })\n const temporary = `${file}.${process.pid}.tmp`\n fs.writeFileSync(temporary, JSON.stringify(cache))\n fs.renameSync(temporary, file)\n } catch {\n return\n }\n}\n\nasync function fetchOnce(\n fetchImpl: FetchLike,\n url: string,\n etag: string | null,\n timeoutMs: number,\n): Promise<FetchResponseLike> {\n const headers: Record<string, string> = { accept: 'application/json' }\n if (etag) headers['if-none-match'] = etag\n return fetchImpl(url, { headers, signal: AbortSignal.timeout(timeoutMs) })\n}\n\nasync function fetchWithRetry(\n fetchImpl: FetchLike,\n url: string,\n etag: string | null,\n timeoutMs: number,\n): Promise<FetchResponseLike> {\n let lastError: unknown = null\n for (let attempt = 0; attempt < 2; attempt += 1) {\n try {\n const response = await fetchOnce(fetchImpl, url, etag, timeoutMs)\n if (response.status >= 500) {\n lastError = new Error(`${url} answered ${response.status}`)\n continue\n }\n return response\n } catch (error: unknown) {\n lastError = error\n }\n }\n throw lastError instanceof Error ? lastError : new Error(String(lastError))\n}\n\nasync function fetchJson<T>(\n fetchImpl: FetchLike,\n url: string,\n etag: string | null,\n timeoutMs: number,\n parse: (value: unknown) => T | null,\n): Promise<{ notModified: true } | { notModified: false; value: T; etag: string | null }> {\n const response = await fetchWithRetry(fetchImpl, url, etag, timeoutMs)\n if (response.status === 304) return { notModified: true }\n if (!response.ok) throw new Error(`${url} answered ${response.status}`)\n const value = parse(await response.json())\n if (value === null) throw new Error(`${url} is not a module index this CLI understands`)\n return { notModified: false, value, etag: response.headers.get('etag') }\n}\n\nfunction searcherOf(index: CatalogIndex, builtIn: BuiltInIndex | null): CatalogSearcher | null {\n try {\n return createCatalogSearcher(index, builtIn)\n } catch {\n return null\n }\n}\n\nexport async function loadIndex(options: LoadIndexOptions): Promise<LoadedModuleIndex> {\n const url = options.url ?? DEFAULT_MODULES_INDEX_URL\n const builtInUrl = options.builtInUrl ?? defaultBuiltInUrl(url)\n const timeoutMs = options.timeoutMs ?? MODULE_INDEX_TIMEOUT_MS\n const ttlMs = options.ttlMs ?? MODULE_INDEX_TTL_MS\n const fetchImpl: FetchLike = options.fetchImpl ?? ((input, init) => fetch(input, init))\n const now = options.now ?? (() => Date.now())\n const cacheFile = path.join(options.appRoot, MODULE_INDEX_CACHE_FILE)\n const cached = readCache(cacheFile)\n let usable = cached && cached.url === url && cached.builtInUrl === builtInUrl ? cached : null\n\n // a copy without the built-in list is not fresh: the next run asks for built-in.json again\n if (usable && usable.builtIn && now() - Date.parse(usable.fetchedAt) < ttlMs) {\n const searcher = searcherOf(usable.index, usable.builtIn)\n if (searcher) {\n return { index: usable.index, builtIn: usable.builtIn, builtInUnavailable: null, source: 'cache', fetchedAt: usable.fetchedAt, searcher }\n }\n // a copy this CLI cannot search is no copy: download a new one\n usable = null\n }\n\n try {\n const indexResult = await fetchJson(fetchImpl, url, usable?.etag ?? null, timeoutMs, parseCatalogIndex)\n let index: CatalogIndex\n let etag: string | null\n if (indexResult.notModified) {\n if (!usable) throw new Error(`${url} answered 304 without a cached copy`)\n index = usable.index\n etag = usable.etag\n } else {\n index = indexResult.value\n etag = indexResult.etag\n }\n let builtIn = usable?.builtIn ?? null\n let builtInEtag = usable?.builtInEtag ?? null\n let builtInUnavailable: string | null = null\n try {\n const builtInResult = await fetchJson(\n fetchImpl,\n builtInUrl,\n usable?.builtIn ? builtInEtag : null,\n timeoutMs,\n parseBuiltInIndex,\n )\n if (!builtInResult.notModified) {\n builtIn = builtInResult.value\n builtInEtag = builtInResult.etag\n }\n } catch (error: unknown) {\n builtIn = usable?.builtIn ?? null\n if (!builtIn) builtInUnavailable = error instanceof Error ? error.message : String(error)\n }\n // built before the copy is saved, so an index the CLI cannot search never replaces one it can\n const searcher = createCatalogSearcher(index, builtIn)\n const fetchedAt = new Date(now()).toISOString()\n writeCache(cacheFile, {\n cacheVersion: 1,\n url,\n builtInUrl,\n fetchedAt,\n etag,\n builtInEtag,\n index,\n builtIn,\n })\n return { index, builtIn, builtInUnavailable, source: 'network', fetchedAt, searcher }\n } catch (error: unknown) {\n const searcher = usable ? searcherOf(usable.index, usable.builtIn) : null\n if (usable && searcher) {\n return {\n index: usable.index,\n builtIn: usable.builtIn,\n builtInUnavailable: usable.builtIn ? null : 'the cached copy has no built-in module list',\n source: 'stale-cache',\n fetchedAt: usable.fetchedAt,\n searcher,\n }\n }\n const detail = error instanceof Error ? error.message : String(error)\n throw new ModuleIndexUnavailableError(detail)\n }\n}\n\nexport type AppContext = {\n omVersion: string | null\n enabledModules: ModuleEntry[] | null\n}\n\nfunction readJsonFile(file: string): unknown {\n try {\n return JSON.parse(fs.readFileSync(file, 'utf8'))\n } catch {\n return null\n }\n}\n\nexport function detectAppLine(appRoot: string): string | null {\n const installed = readJsonFile(path.join(appRoot, 'node_modules', '@open-mercato', 'core', 'package.json'))\n if (isRecord(installed) && isString(installed.version)) return installed.version\n const manifest = readJsonFile(path.join(appRoot, 'package.json'))\n if (!isRecord(manifest)) return null\n for (const field of ['dependencies', 'devDependencies'] as const) {\n const deps = manifest[field]\n if (isRecord(deps) && isString(deps[CORE_PACKAGE])) {\n const range = deps[CORE_PACKAGE].trim()\n const match = /\\d+\\.\\d+(?:\\.\\d+)?(?:-[0-9A-Za-z.-]+)?/.exec(range)\n return match ? match[0] : null\n }\n }\n return null\n}\n\nexport type ReadEnabledModules = (modulesPath: string, env: NodeJS.ProcessEnv) => ModuleEntry[]\n\nexport function detectAppContext(\n appRoot: string,\n readEnabled: ReadEnabledModules | null,\n env: NodeJS.ProcessEnv = process.env,\n): AppContext {\n const modulesPath = path.join(appRoot, 'src', 'modules.ts')\n let enabledModules: ModuleEntry[] | null = null\n if (readEnabled && fs.existsSync(modulesPath)) {\n try {\n enabledModules = readEnabled(modulesPath, env)\n } catch {\n enabledModules = null\n }\n }\n return { omVersion: detectAppLine(appRoot), enabledModules }\n}\n\nexport type ModuleSearchJson = {\n schemaVersion: 1\n query: string\n line: string | null\n omVersion: string | null\n results: ModuleCard[]\n builtIn: BuiltInCard[]\n /** Present (true) when the built-in module list could not be loaded, so `builtIn` may miss modules core has. */\n builtInUnavailable?: true\n total: number\n hidden: number\n dataVersion: string\n generatedAt: string\n receiptSource: ReceiptSource | null\n /** Set when nothing matched the query and the results are for this spelling of it (`stirpe` -> `stripe`). */\n didYouMean?: string\n}\n\nexport type ModuleSearchView = {\n json: ModuleSearchJson\n entries: Map<string, CatalogEntry>\n lineNote: string | null\n allLines: boolean\n}\n\nexport type ModuleSearchRequest = {\n query: string | null\n category?: string | null\n kinds?: ('module' | 'provider' | 'library' | 'tool' | 'built-in')[] | null\n allLines?: boolean\n limit?: number\n}\n\nfunction builtInEnabled(id: string, enabled: ModuleEntry[] | null): boolean | null {\n if (!enabled) return null\n return enabled.some((coreModule) => coreModule.id === id && (coreModule.from ?? CORE_PACKAGE) === CORE_PACKAGE)\n}\n\nexport function buildSearchView(\n searcher: CatalogSearcher,\n request: ModuleSearchRequest,\n app: AppContext,\n): ModuleSearchView {\n const resolved = app.omVersion ? resolveLine(app.omVersion, searcher.index.lines) : null\n const rankInput = {\n query: request.query ?? '',\n ...(app.omVersion ? { omVersion: app.omVersion } : {}),\n ...(request.category ? { category: request.category } : {}),\n ...(request.kinds ? { kind: request.kinds } : {}),\n }\n const unfiltered = rankModules(searcher, rankInput)\n const line = unfiltered.line\n const allLines = request.allLines === true || line === null\n const ranked = allLines ? unfiltered : rankModules(searcher, { ...rankInput, verifiedModulesOnly: true })\n const kept = ranked.modules\n const hidden = allLines ? 0 : unfiltered.modules.filter(\n (hit) => hit.meta.entry.kind === 'module' && (line === null || !hit.meta.entry.version.recommended[line]),\n ).length\n const limit = Math.min(SEARCH_MAX_LIMIT, Math.max(1, Math.floor(request.limit ?? 10)))\n const results = kept.slice(0, limit).map((hit) => toModuleCard(searcher, hit, line, ranked.latest))\n const entries = new Map<string, CatalogEntry>()\n for (const hit of kept.slice(0, limit)) entries.set(hit.meta.entry.locator, hit.meta.entry)\n const builtIn = ranked.builtIn.map((card) => ({ ...card, enabled: builtInEnabled(card.id, app.enabledModules) }))\n const lineNote =\n resolved && resolved.line === null\n ? `Open Mercato ${app.omVersion} is not a line the catalog tests (${resolved.note ?? 'unknown version'}). Showing modules for every line.`\n : null\n return {\n json: {\n schemaVersion: 1,\n query: request.query ?? '',\n line,\n omVersion: app.omVersion,\n results,\n builtIn,\n total: unfiltered.modules.length,\n hidden,\n dataVersion: searcher.index.dataVersion,\n generatedAt: searcher.index.generatedAt,\n receiptSource: receiptSourceOf(searcher),\n ...(ranked.didYouMean ? { didYouMean: ranked.didYouMean } : {}),\n },\n entries,\n lineNote,\n allLines,\n }\n}\n\nconst ORIGIN_LABEL: Record<string, string> = { official: 'official', community: 'community', '3rd-party': '3rd-party' }\n\nfunction levelLabel(entry: CatalogEntry, line: string | null): string {\n const level = entry.quality ? 'OM QUALITY' : entry.tier === 'verified' ? 'VERIFIED' : 'LISTED'\n if (line === null) return level\n const status = statusOn(entry, line)\n return status === 'verified' ? `${level} \u00B7 ${line}` : `${level} \u00B7 ${status} on ${line}`\n}\n\nfunction policyLabel(policy: AgentPolicy | null): string | null {\n if (!policy) return null\n const reasons = policy.reasons.slice(0, 2).map((reason) => reason.code.toLowerCase().replace(/_/g, '-'))\n return reasons.length > 0 ? `${policy.decision} (${reasons.join(', ')})` : policy.decision\n}\n\nfunction licenseLabel(entry: CatalogEntry): string {\n return entry.license.spdx ?? entry.license.kind\n}\n\nconst steps = (command: string) => command.split(' && ')\n\n/**\n * The install line a person copies: the command, then `yarn generate` after a `yarn add` of a library or a provider\n * and after `npx create-mercato-module install` (`yarn mercato module add` runs it itself), then `yarn db:migrate`\n * after a module install (also the enterprise step) that adds tables or migrations or whose anatomy is unknown.\n * `--json` keeps the bare command (tools@1).\n */\nexport function withFollowUpSteps(command: string, entry: CatalogEntry): string {\n const cmm = command.startsWith('npx create-mercato-module install')\n const ofModules = entry.kind === 'module' || entry.kind === 'provider'\n const moduleInstall =\n (cmm && ofModules) || command.includes('yarn mercato module add') || command.startsWith(ENTERPRISE_ENABLE_STEP)\n const addsPackage = command.startsWith('yarn add ') && (entry.kind === 'library' || entry.kind === 'provider')\n const generate = (addsPackage || cmm) && !steps(command).includes('yarn generate')\n const tables = entry.anatomy?.lists?.tables\n const adds = tables === undefined || tables.length > 0 || (entry.anatomy?.counts?.migrations ?? 0) > 0\n const migrate = moduleInstall && adds && !steps(command).includes('yarn db:migrate')\n return [command, ...(generate ? ['yarn generate'] : []), ...(migrate ? ['yarn db:migrate'] : [])].join(' && ')\n}\n\nfunction installText(\n install: Pick<ModuleCard, 'install' | 'presetNote' | 'installEmptyPreset'>,\n entry: CatalogEntry,\n): string {\n if (install.presetNote === undefined) return withFollowUpSteps(install.install, entry)\n const empty = install.installEmptyPreset ?? null\n return newAppInstallText(entry.moduleIds, empty === null ? null : withFollowUpSteps(empty, entry))\n}\n\nfunction entryHeading(entry: CatalogEntry, line: string | null): string {\n const name = entry.npm ?? entry.locator.replace(/^github:/, '')\n const version = installVersion(entry, line)\n const origin = ORIGIN_LABEL[entry.type] ?? entry.type\n return `${name}${version ? ` ${version}` : ''} ${levelLabel(entry, line)} ${origin} \u00B7 ${licenseLabel(entry)}`\n}\n\n/** `today` (YYYY-MM-DD) decides whether `installable from` is still ahead (Yarn age gate). */\nexport function renderSearchHuman(view: ModuleSearchView, today: string = new Date().toISOString().slice(0, 10)): string[] {\n const { json } = view\n const out: string[] = []\n if (view.lineNote) out.push(view.lineNote, '')\n if (json.didYouMean) out.push(`Nothing matched \"${json.query}\". Showing results for \"${json.didYouMean}\".`, '')\n if (json.builtIn.length > 0) {\n out.push('Already in Open Mercato core:')\n const idWidth = Math.max(...json.builtIn.map((card) => card.id.length))\n const titleWidth = Math.max(...json.builtIn.map((card) => card.title.length))\n for (const card of json.builtIn) {\n const action = card.enabled ? 'Already enabled.' : card.enabled === false ? card.install : (card.presetNote ?? card.install)\n out.push(` ${card.id.padEnd(idWidth)} ${card.title.padEnd(titleWidth)} ${action}`)\n }\n out.push('')\n }\n for (const card of json.results) {\n const entry = view.entries.get(card.locator)\n if (!entry) continue\n out.push(entryHeading(entry, json.line))\n if (card.summary) out.push(` ${card.summary}`)\n const details = [\n card.matchedOn.length > 0 ? `matched: ${card.matchedOn.join(', ')}` : null,\n policyLabel(card.agentPolicy) ? `agent policy: ${policyLabel(card.agentPolicy)}` : null,\n ].filter((item): item is string => item !== null)\n if (details.length > 0) out.push(` ${details.join(' \u00B7 ')}`)\n if (card.installableFrom && card.installableFrom > today && card.status !== 'verified') {\n out.push(` installable from: ${card.installableFrom}`)\n }\n out.push(` install: ${installText(card, entry)}`)\n out.push(` more: ${card.card}`)\n out.push('')\n }\n const hiddenLine =\n json.hidden > 0 && json.line !== null\n ? json.hidden === 1\n ? `1 more module is listed but not verified on ${json.line}. Use --all-lines.`\n : `${json.hidden} more modules are listed but not verified on ${json.line}. Use --all-lines.`\n : null\n if (json.results.length === 0 && json.builtIn.length === 0) {\n const what = json.query ? `\"${json.query}\"` : 'the filters'\n out.push(hiddenLine ? `No modules verified on ${json.line} matched ${what}.` : `No modules matched ${what}.`)\n if (hiddenLine) out.push(hiddenLine)\n out.push(`Browse the catalog: ${MODULES_SITE_URL}`)\n } else if (hiddenLine) {\n out.push(hiddenLine)\n }\n while (out.length > 0 && out[out.length - 1] === '') out.pop()\n return out\n}\n\nexport type ModuleInfoJson =\n | {\n schemaVersion: 1\n line: string | null\n entry: CatalogEntry | null\n candidates: string[]\n anatomy: null\n warnings: string[]\n untrusted: null\n card: string | null\n }\n | { schemaVersion: 1; error: string; candidates: string[] }\n\nexport type ModuleInfoView = {\n json: ModuleInfoJson\n builtIn: BuiltInModule | null\n builtInEnabled: boolean | null\n found: boolean\n}\n\nexport function buildInfoView(searcher: CatalogSearcher, ref: string, app: AppContext, today: string): ModuleInfoView {\n const builtInModules = searcher.builtIn?.modules ?? []\n const entries = searcher.index.entries\n const latest = latestLine(searcher.index.lines)?.key ?? null\n const line = app.omVersion ? resolveLine(app.omVersion, searcher.index.lines).line : latest\n const resolved = resolveRef(ref, entries, builtInModules)\n if (resolved.kind === 'builtin') {\n const coreModule = resolved.builtIn\n return {\n json: {\n schemaVersion: 1,\n line,\n entry: null,\n candidates: resolved.candidates,\n anatomy: null,\n warnings: builtInWarnings(coreModule, line),\n untrusted: null,\n card: cardUrl(searcher, coreModule.page),\n },\n builtIn: coreModule,\n builtInEnabled: builtInEnabled(coreModule.id, app.enabledModules),\n found: true,\n }\n }\n if (resolved.kind === 'none') {\n const id = stripVersion(ref).name.trim().toLowerCase()\n const shared = entries.filter((entry) => entry.moduleIds.includes(id))\n if (shared.length > 1) {\n return {\n json: {\n schemaVersion: 1,\n line,\n entry: null,\n candidates: resolved.candidates,\n anatomy: null,\n warnings: [sharedModuleIdWarning(id, shared.length)],\n untrusted: null,\n card: null,\n },\n builtIn: null,\n builtInEnabled: null,\n found: true,\n }\n }\n return {\n json: { schemaVersion: 1, error: unknownModuleMessage(ref, resolved.candidates), candidates: resolved.candidates },\n builtIn: null,\n builtInEnabled: null,\n found: false,\n }\n }\n const entry = resolved.entry\n return {\n json: {\n schemaVersion: 1,\n line,\n entry,\n candidates: [],\n anatomy: null,\n warnings: entryWarnings(entry, today, line, receiptSourceOf(searcher)),\n untrusted: null,\n card: cardUrl(searcher, entry.links.md),\n },\n builtIn: null,\n builtInEnabled: null,\n found: true,\n }\n}\n\nfunction plural(count: number, one: string, many: string): string {\n return `${count} ${count === 1 ? one : many}`\n}\n\nfunction anatomyLine(entry: CatalogEntry): string | null {\n const lists = entry.anatomy?.lists\n if (!lists) return null\n const parts = [\n lists.tables && lists.tables.length > 0 ? plural(lists.tables.length, 'table', 'tables') : null,\n lists.aclFeatures && lists.aclFeatures.length > 0 ? plural(lists.aclFeatures.length, 'permission', 'permissions') : null,\n lists.eventsEmitted && lists.eventsEmitted.length > 0 ? `emits ${plural(lists.eventsEmitted.length, 'event', 'events')}` : null,\n lists.publicRoutes && lists.publicRoutes.length > 0\n ? plural(lists.publicRoutes.length, 'public route', 'public routes')\n : null,\n ].filter((item): item is string => item !== null)\n return parts.length > 0 ? parts.join(' \u00B7 ') : null\n}\n\nexport function renderInfoHuman(view: ModuleInfoView, searcher: CatalogSearcher): string[] {\n const { json } = view\n if ('error' in json) return [json.error]\n const out: string[] = []\n if (view.builtIn) {\n const coreModule = view.builtIn\n out.push(`${coreModule.id} is built into Open Mercato core: ${coreModule.title}.`)\n if (coreModule.description) out.push(` ${coreModule.description}`)\n const presetNote = view.builtInEnabled === false ? null : builtInPresetText(coreModule, json.line)\n if (view.builtInEnabled) out.push(' Already enabled.')\n else if (presetNote) out.push(` ${presetNote}`)\n else out.push(` enable: ${coreModule.install || builtInEnableCommand(coreModule.id)}`)\n if (json.candidates.length > 0) out.push(` catalog modules with the same id: ${json.candidates.join(', ')}`)\n if (json.card) out.push(` more: ${json.card}`)\n return out\n }\n const entry = json.entry\n if (!entry) {\n out.push(...json.warnings)\n if (json.candidates.length > 0) out.push(`Candidates: ${json.candidates.join(', ')}`)\n return out\n }\n const line = json.line\n out.push(entryHeading(entry, line))\n out.push(` ${entry.title}: ${entry.summary}`)\n if (entry.whenToUse) out.push(` ${entry.whenToUse}`)\n const lines = searcher.index.lines.map((item) => `${item.key} ${statusOn(entry, item.key)}`)\n if (lines.length > 0) out.push(` lines: ${lines.join(' \u00B7 ')}`)\n const policy = line ? (entry.agentPolicy[line] ?? null) : null\n if (policy && line) {\n out.push(` agent policy on ${line}: ${policy.decision}`)\n for (const reason of policy.reasons.slice(0, 5)) out.push(` ${reason.code}: ${reason.text}`)\n }\n const adds = anatomyLine(entry)\n if (adds) out.push(` adds: ${adds}`)\n const lists = entry.anatomy?.lists\n const settings = [...(lists?.env ?? []), ...(lists?.credentials ?? []).map((credential) => credential.key)]\n if (settings.length > 0) out.push(` needs settings: ${settings.slice(0, 10).join(', ')}`)\n if (lists?.hosts && lists.hosts.length > 0) out.push(` talks to: ${lists.hosts.slice(0, 10).join(', ')}`)\n const install = installFields(entry, line, latestLine(searcher.index.lines)?.key ?? null, peerLookup(searcher))\n out.push(` install: ${installText(install, entry)}`)\n for (const warning of json.warnings) out.push(` warning: ${warning}`)\n out.push(` install plan: ${json.card ?? entry.links.md}`)\n out.push(` more: ${cardUrl(searcher, entry.links.page)}`)\n return out\n}\n\nexport type ModuleSearchDeps = {\n appRoot?: string\n env?: NodeJS.ProcessEnv\n fetchImpl?: FetchLike\n now?: () => number\n stdout?: (line: string) => void\n stderr?: (line: string) => void\n readEnabledModules?: ReadEnabledModules | null\n}\n\nconst SEARCH_USAGE =\n 'Usage: yarn mercato module search [<query>] [--category <id>] [--kind <kind>] [--all-lines] [--limit <n>] [--json] (a query, a --category or a --kind)'\nconst INFO_USAGE = 'Usage: yarn mercato module info <package-or-module-id> [--json]'\n\nexport const BUILT_IN_UNAVAILABLE_MESSAGE =\n 'Built-in module list unavailable; results may miss modules already in Open Mercato core.'\n\ntype LoadedForCommand = { searcher: CatalogSearcher; app: AppContext; builtInUnavailable: boolean }\n\nasync function loadForCommand(deps: ModuleSearchDeps): Promise<LoadedForCommand> {\n const env = deps.env ?? process.env\n const appRoot = deps.appRoot ?? createResolver().getAppDir()\n const stderr = deps.stderr ?? ((line: string) => console.error(line))\n const url = env.OM_MODULES_INDEX_URL?.trim() || DEFAULT_MODULES_INDEX_URL\n const builtInUrl = env.OM_MODULES_BUILTIN_URL?.trim() || undefined\n const loaded = await loadIndex({\n appRoot,\n url,\n ...(builtInUrl ? { builtInUrl } : {}),\n ...(deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}),\n ...(deps.now ? { now: deps.now } : {}),\n })\n if (loaded.source === 'stale-cache') stderr(`Using cached module index from ${loaded.fetchedAt}.`)\n if (loaded.builtInUnavailable !== null) {\n stderr(BUILT_IN_UNAVAILABLE_MESSAGE)\n stderr(`Reason: ${loaded.builtInUnavailable}`)\n }\n const app = detectAppContext(appRoot, deps.readEnabledModules ?? null, env)\n return {\n searcher: loaded.searcher,\n app,\n builtInUnavailable: loaded.builtInUnavailable !== null,\n }\n}\n\nfunction handleLoadError(error: unknown, stderr: (line: string) => void): number {\n if (error instanceof ModuleIndexUnavailableError) {\n stderr(MODULE_INDEX_UNAVAILABLE_MESSAGE)\n stderr(`Reason: ${error.detail}`)\n return MODULE_INDEX_UNAVAILABLE_EXIT_CODE\n }\n throw error\n}\n\nexport async function runModuleSearch(args: string[], deps: ModuleSearchDeps = {}): Promise<number> {\n const stdout = deps.stdout ?? ((line: string) => console.log(line))\n const stderr = deps.stderr ?? ((line: string) => console.error(line))\n const parsed = parseModuleSearchArgs(args)\n if (!parsed.query && !parsed.category && !parsed.kinds) {\n stderr(SEARCH_USAGE)\n return 1\n }\n let loaded: LoadedForCommand\n try {\n loaded = await loadForCommand(deps)\n } catch (error: unknown) {\n return handleLoadError(error, stderr)\n }\n const view = buildSearchView(\n loaded.searcher,\n { query: parsed.query, category: parsed.category, kinds: parsed.kinds, allLines: parsed.allLines, limit: parsed.limit },\n loaded.app,\n )\n if (loaded.builtInUnavailable) view.json.builtInUnavailable = true\n const today = new Date(deps.now ? deps.now() : Date.now()).toISOString().slice(0, 10)\n if (parsed.json) stdout(JSON.stringify(view.json, null, 2))\n else for (const line of renderSearchHuman(view, today)) stdout(line)\n return 0\n}\n\nexport async function runModuleInfo(args: string[], deps: ModuleSearchDeps = {}): Promise<number> {\n const stdout = deps.stdout ?? ((line: string) => console.log(line))\n const stderr = deps.stderr ?? ((line: string) => console.error(line))\n const parsed = parseModuleInfoArgs(args)\n if (!parsed.ref) {\n stderr(INFO_USAGE)\n return 1\n }\n let loaded: LoadedForCommand\n try {\n loaded = await loadForCommand(deps)\n } catch (error: unknown) {\n return handleLoadError(error, stderr)\n }\n const today = new Date(deps.now ? deps.now() : Date.now()).toISOString().slice(0, 10)\n const view = buildInfoView(loaded.searcher, parsed.ref, loaded.app, today)\n if (parsed.json) stdout(JSON.stringify(view.json, null, 2))\n else if (view.found) for (const line of renderInfoHuman(view, loaded.searcher)) stdout(line)\n else for (const line of renderInfoHuman(view, loaded.searcher)) stderr(line)\n return view.found ? 0 : 1\n}\n"],
5
- "mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AAEjB,SAAS,sBAAsB;AAC/B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAUK;AACP,SAAS,qBAAqB,6BAA6B;AAEpD,MAAM,4BAA4B;AAClC,MAAM,mBAAmB;AACzB,MAAM,0BAA0B;AAChC,MAAM,sBAAsB,KAAK,KAAK;AACtC,MAAM,0BAA0B,KAAK,KAAK,YAAY,SAAS,oBAAoB;AACnF,MAAM,qCAAqC;AAC3C,MAAM,mCACX;AA6CK,MAAM,oCAAoC,MAAM;AAAA,EAGrD,YAAY,QAAgB;AAC1B,UAAM,GAAG,gCAAgC,KAAK,MAAM,GAAG;AACvD,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,EAChB;AACF;AAEA,MAAM,WAAW,CAAC,UAChB,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AACrE,MAAM,WAAW,CAAC,UAAoC,OAAO,UAAU;AACvE,MAAM,gBAAgB,CAAC,UAAsC,MAAM,QAAQ,KAAK,KAAK,MAAM,MAAM,QAAQ;AACzG,MAAM,mBAAmB,CAAC,UAA2C,UAAU,QAAQ,SAAS,KAAK;AACrG,MAAM,YAAY,CAAC,OAAgB,SAA+C,MAAM,QAAQ,KAAK,KAAK,MAAM,MAAM,IAAI;AAC1H,MAAM,aAAa,CAAC,OAAgB,SAClC,SAAS,KAAK,KAAK,OAAO,OAAO,KAAK,EAAE,MAAM,IAAI;AACpD,MAAM,aAAa,CAAC,OAAgB,UAAgD,UAAU,UAAa,MAAM,KAAK;AAEtH,MAAM,gBAAgB,CAAC,eAAe,iBAAiB,kBAAkB,OAAO,SAAS,UAAU,cAAc;AAGjH,SAAS,UAAU,OAAyB;AAC1C,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,MAAI,CAAC,SAAS,KAAK,EAAG,QAAO;AAC7B,QAAM,QAAQ,MAAM;AACpB,MAAI,UAAU,OAAW,QAAO;AAChC,SACE,SAAS,KAAK,KACd,cAAc,MAAM,CAAC,QAAQ,WAAW,MAAM,GAAG,GAAG,aAAa,CAAC,KAClE;AAAA,IAAW,MAAM;AAAA,IAAa,CAAC,gBAC7B,UAAU,aAAa,CAAC,eAAe,SAAS,UAAU,KAAK,SAAS,WAAW,GAAG,CAAC;AAAA,EACzF;AAEJ;AAEA,SAAS,cAAc,OAAyB;AAC9C,SACE,SAAS,KAAK,KACd,SAAS,MAAM,QAAQ,KACvB,UAAU,MAAM,SAAS,CAAC,WAAW,SAAS,MAAM,KAAK,SAAS,OAAO,IAAI,KAAK,SAAS,OAAO,IAAI,CAAC;AAE3G;AAEA,SAAS,WAAW,OAAyB;AAC3C,SACE,SAAS,KAAK,KACd,SAAS,MAAM,IAAI,KACnB,iBAAiB,MAAM,KAAK,KAC5B,cAAc,MAAM,KAAK,KACzB,WAAW,MAAM,QAAQ,CAAC,WAAW,WAAW,QAAQ,QAAQ,CAAC;AAErE;AAEA,SAAS,SAAS,OAAyB;AACzC,SACE,SAAS,KAAK,KACd;AAAA,IACE,MAAM;AAAA,IACN,CAAC,SACC,SAAS,IAAI,KACb,SAAS,KAAK,MAAM,KACpB,WAAW,KAAK,IAAI,gBAAgB,KACpC,WAAW,KAAK,eAAe,gBAAgB;AAAA,EACnD,KACA,cAAc,MAAM,UAAU,KAC9B,WAAW,MAAM,UAAU,UAAU,KACrC,WAAW,MAAM,WAAW,aAAa,KACzC,WAAW,MAAM,eAAe,aAAa,KAC7C,WAAW,MAAM,OAAO,CAAC,UAAU,WAAW,OAAO,QAAQ,CAAC,KAC9D;AAAA,IAAW,MAAM;AAAA,IAAW,CAAC,cAC3B,WAAW,WAAW,CAAC,SAAS,SAAS,IAAI,KAAK,SAAS,KAAK,MAAM,KAAK,iBAAiB,KAAK,aAAa,CAAC;AAAA,EACjH;AAEJ;AAEA,SAAS,UAAU,OAAyB;AAC1C,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,SACE,SAAS,KAAK,KACd,WAAW,MAAM,aAAa,CAAC,SAAS,SAAS,IAAI,KAAK,SAAS,KAAK,MAAM,KAAK,cAAc,KAAK,OAAO,CAAC,KAC9G,WAAW,MAAM,SAAS,CAAC,SAAS,SAAS,IAAI,KAAK,SAAS,KAAK,MAAM,KAAK,cAAc,KAAK,OAAO,CAAC,KAC1G,WAAW,MAAM,SAAS,QAAQ;AAEtC;AAIA,SAAS,eAAe,OAAuC;AAC7D,MAAI,CAAC,SAAS,KAAK,EAAG,QAAO;AAC7B,QAAM,UAAU,MAAM;AACtB,QAAM,UAAU,MAAM;AACtB,QAAM,QAAQ,MAAM;AACpB,QAAM,UAAU,MAAM;AACtB,SACE,SAAS,MAAM,OAAO,KACtB,SAAS,MAAM,IAAI,MAClB,MAAM,QAAQ,QAAQ,SAAS,MAAM,GAAG,MACzC,SAAS,MAAM,KAAK,KACpB,SAAS,MAAM,OAAO,KACtB,SAAS,MAAM,SAAS,KACxB,SAAS,MAAM,IAAI,KACnB,SAAS,MAAM,IAAI,KACnB,SAAS,MAAM,QAAQ,KACvB,cAAc,MAAM,IAAI,KACxB,UAAU,MAAM,WAAW,CAAC,aAAa,SAAS,QAAQ,KAAK,SAAS,SAAS,IAAI,KAAK,SAAS,SAAS,KAAK,CAAC,KAClH,cAAc,MAAM,SAAS,KAC7B;AAAA,IAAW,MAAM;AAAA,IAAU,CAAC,aAC1B;AAAA,MACE;AAAA,MACA,CAAC,gBAAgB,SAAS,WAAW,KAAK,SAAS,YAAY,GAAG,KAAK,WAAW,YAAY,YAAY,gBAAgB;AAAA,IAC5H;AAAA,EACF,KACA,cAAc,MAAM,WAAW,KAC/B,SAAS,MAAM,MAAM,KACrB,OAAO,MAAM,WAAW,aACxB,OAAO,MAAM,aAAa,aAC1B,iBAAiB,MAAM,IAAI,KAC3B,SAAS,OAAO,KAChB,iBAAiB,QAAQ,IAAI,KAC7B,SAAS,QAAQ,IAAI,KACrB,SAAS,OAAO,KAChB,iBAAiB,QAAQ,MAAM,KAC/B,iBAAiB,QAAQ,OAAO,KAChC,iBAAiB,QAAQ,eAAe,KACxC,iBAAiB,QAAQ,MAAM,KAC/B,WAAW,QAAQ,aAAa,gBAAgB,KAChD,SAAS,MAAM,MAAM,KACrB,UAAU,MAAM,OAAO,KACvB,UAAU,MAAM,OAAO,KACvB,WAAW,MAAM,aAAa,aAAa,KAC3C,SAAS,OAAO,KAChB,SAAS,QAAQ,GAAG,KACpB,WAAW,QAAQ,YAAY,gBAAgB,KAC/C,SAAS,KAAK,KACd,SAAS,MAAM,IAAI,KACnB,SAAS,MAAM,EAAE;AAErB;AAEO,SAAS,kBAAkB,OAAqC;AACrE,MAAI,CAAC,SAAS,KAAK,KAAK,MAAM,kBAAkB,EAAG,QAAO;AAC1D,MAAI,CAAC,SAAS,MAAM,WAAW,KAAK,CAAC,SAAS,MAAM,WAAW,KAAK,CAAC,SAAS,MAAM,IAAI,EAAG,QAAO;AAClG,MAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,KAAK,CAAC,MAAM,QAAQ,MAAM,OAAO,EAAG,QAAO;AACzE,MAAI,CAAC,WAAW,MAAM,UAAU,QAAQ,EAAG,QAAO;AAClD,MAAI,CAAC,MAAM,MAAM,MAAM,CAAC,SAAS,SAAS,IAAI,KAAK,SAAS,KAAK,GAAG,KAAK,SAAS,KAAK,OAAO,KAAK,SAAS,KAAK,MAAM,CAAC,GAAG;AACzH,WAAO;AAAA,EACT;AACA,MAAI,CAAC,MAAM,QAAQ,MAAM,cAAc,EAAG,QAAO;AACjD,QAAM,QAAQ,MAAM,MAAM;AAAA,IACxB,CAAC,SACC,KAAK,WAAW,YAAY,KAAK,WAAW,cAAc,KAAK,WAAW;AAAA,EAC9E;AACA,SAAO,EAAE,GAAI,OAAwB,MAAM;AAC7C;AAEO,SAAS,kBAAkB,OAAqC;AACrE,MAAI,CAAC,SAAS,KAAK,KAAK,MAAM,kBAAkB,KAAK,CAAC,SAAS,MAAM,WAAW,KAAK,CAAC,MAAM,QAAQ,MAAM,OAAO,GAAG;AAClH,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,MAAM,QAAQ;AAAA,IAC1B,CAAC,eACC,SAAS,UAAU,KACnB,SAAS,WAAW,EAAE,KACtB,SAAS,WAAW,KAAK,KACzB,SAAS,WAAW,WAAW,KAC/B,SAAS,WAAW,QAAQ,KAC5B,SAAS,WAAW,OAAO,KAC3B,SAAS,WAAW,IAAI,KACxB,WAAW,WAAW,UAAU,aAAa,KAC7C,WAAW,WAAW,WAAW,aAAa,KAC9C,WAAW,WAAW,eAAe,aAAa,KAClD,UAAU,WAAW,OAAO;AAAA,EAChC;AACA,SAAO,QAAS,QAAyB;AAC3C;AAEA,SAAS,eAAe,OAAkC;AACxD,MAAI,CAAC,SAAS,KAAK,KAAK,MAAM,iBAAiB,EAAG,QAAO;AACzD,MAAI,CAAC,SAAS,MAAM,GAAG,KAAK,CAAC,SAAS,MAAM,UAAU,KAAK,CAAC,SAAS,MAAM,SAAS,EAAG,QAAO;AAC9F,QAAM,QAAQ,kBAAkB,MAAM,KAAK;AAC3C,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,UAAU,MAAM,YAAY,OAAO,OAAO,kBAAkB,MAAM,OAAO;AAC/E,SAAO;AAAA,IACL,cAAc;AAAA,IACd,KAAK,MAAM;AAAA,IACX,YAAY,MAAM;AAAA,IAClB,WAAW,MAAM;AAAA,IACjB,MAAM,SAAS,MAAM,IAAI,IAAI,MAAM,OAAO;AAAA,IAC1C,aAAa,SAAS,MAAM,WAAW,IAAI,MAAM,cAAc;AAAA,IAC/D;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,kBAAkB,UAA0B;AAC1D,QAAM,MAAM,IAAI,IAAI,QAAQ;AAC5B,MAAI,SAAS;AACb,MAAI,OAAO;AACX,MAAI,WAAW,IAAI,SAAS,QAAQ,UAAU,eAAe;AAC7D,SAAO,IAAI,SAAS;AACtB;AAEA,SAAS,UAAU,MAAgC;AACjD,MAAI;AACF,WAAO,eAAe,KAAK,MAAM,GAAG,aAAa,MAAM,MAAM,CAAC,CAAC;AAAA,EACjE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,WAAW,MAAc,OAAwB;AACxD,MAAI;AACF,OAAG,UAAU,KAAK,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AACpD,UAAM,YAAY,GAAG,IAAI,IAAI,QAAQ,GAAG;AACxC,OAAG,cAAc,WAAW,KAAK,UAAU,KAAK,CAAC;AACjD,OAAG,WAAW,WAAW,IAAI;AAAA,EAC/B,QAAQ;AACN;AAAA,EACF;AACF;AAEA,eAAe,UACb,WACA,KACA,MACA,WAC4B;AAC5B,QAAM,UAAkC,EAAE,QAAQ,mBAAmB;AACrE,MAAI,KAAM,SAAQ,eAAe,IAAI;AACrC,SAAO,UAAU,KAAK,EAAE,SAAS,QAAQ,YAAY,QAAQ,SAAS,EAAE,CAAC;AAC3E;AAEA,eAAe,eACb,WACA,KACA,MACA,WAC4B;AAC5B,MAAI,YAAqB;AACzB,WAAS,UAAU,GAAG,UAAU,GAAG,WAAW,GAAG;AAC/C,QAAI;AACF,YAAM,WAAW,MAAM,UAAU,WAAW,KAAK,MAAM,SAAS;AAChE,UAAI,SAAS,UAAU,KAAK;AAC1B,oBAAY,IAAI,MAAM,GAAG,GAAG,aAAa,SAAS,MAAM,EAAE;AAC1D;AAAA,MACF;AACA,aAAO;AAAA,IACT,SAAS,OAAgB;AACvB,kBAAY;AAAA,IACd;AAAA,EACF;AACA,QAAM,qBAAqB,QAAQ,YAAY,IAAI,MAAM,OAAO,SAAS,CAAC;AAC5E;AAEA,eAAe,UACb,WACA,KACA,MACA,WACA,OACwF;AACxF,QAAM,WAAW,MAAM,eAAe,WAAW,KAAK,MAAM,SAAS;AACrE,MAAI,SAAS,WAAW,IAAK,QAAO,EAAE,aAAa,KAAK;AACxD,MAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,GAAG,GAAG,aAAa,SAAS,MAAM,EAAE;AACtE,QAAM,QAAQ,MAAM,MAAM,SAAS,KAAK,CAAC;AACzC,MAAI,UAAU,KAAM,OAAM,IAAI,MAAM,GAAG,GAAG,6CAA6C;AACvF,SAAO,EAAE,aAAa,OAAO,OAAO,MAAM,SAAS,QAAQ,IAAI,MAAM,EAAE;AACzE;AAEA,SAAS,WAAW,OAAqB,SAAsD;AAC7F,MAAI;AACF,WAAO,sBAAsB,OAAO,OAAO;AAAA,EAC7C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,UAAU,SAAuD;AACrF,QAAM,MAAM,QAAQ,OAAO;AAC3B,QAAM,aAAa,QAAQ,cAAc,kBAAkB,GAAG;AAC9D,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,YAAuB,QAAQ,cAAc,CAAC,OAAO,SAAS,MAAM,OAAO,IAAI;AACrF,QAAM,MAAM,QAAQ,QAAQ,MAAM,KAAK,IAAI;AAC3C,QAAM,YAAY,KAAK,KAAK,QAAQ,SAAS,uBAAuB;AACpE,QAAM,SAAS,UAAU,SAAS;AAClC,MAAI,SAAS,UAAU,OAAO,QAAQ,OAAO,OAAO,eAAe,aAAa,SAAS;AAGzF,MAAI,UAAU,OAAO,WAAW,IAAI,IAAI,KAAK,MAAM,OAAO,SAAS,IAAI,OAAO;AAC5E,UAAM,WAAW,WAAW,OAAO,OAAO,OAAO,OAAO;AACxD,QAAI,UAAU;AACZ,aAAO,EAAE,OAAO,OAAO,OAAO,SAAS,OAAO,SAAS,oBAAoB,MAAM,QAAQ,SAAS,WAAW,OAAO,WAAW,SAAS;AAAA,IAC1I;AAEA,aAAS;AAAA,EACX;AAEA,MAAI;AACF,UAAM,cAAc,MAAM,UAAU,WAAW,KAAK,QAAQ,QAAQ,MAAM,WAAW,iBAAiB;AACtG,QAAI;AACJ,QAAI;AACJ,QAAI,YAAY,aAAa;AAC3B,UAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,GAAG,GAAG,qCAAqC;AACxE,cAAQ,OAAO;AACf,aAAO,OAAO;AAAA,IAChB,OAAO;AACL,cAAQ,YAAY;AACpB,aAAO,YAAY;AAAA,IACrB;AACA,QAAI,UAAU,QAAQ,WAAW;AACjC,QAAI,cAAc,QAAQ,eAAe;AACzC,QAAI,qBAAoC;AACxC,QAAI;AACF,YAAM,gBAAgB,MAAM;AAAA,QAC1B;AAAA,QACA;AAAA,QACA,QAAQ,UAAU,cAAc;AAAA,QAChC;AAAA,QACA;AAAA,MACF;AACA,UAAI,CAAC,cAAc,aAAa;AAC9B,kBAAU,cAAc;AACxB,sBAAc,cAAc;AAAA,MAC9B;AAAA,IACF,SAAS,OAAgB;AACvB,gBAAU,QAAQ,WAAW;AAC7B,UAAI,CAAC,QAAS,sBAAqB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,IAC1F;AAEA,UAAM,WAAW,sBAAsB,OAAO,OAAO;AACrD,UAAM,YAAY,IAAI,KAAK,IAAI,CAAC,EAAE,YAAY;AAC9C,eAAW,WAAW;AAAA,MACpB,cAAc;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AACD,WAAO,EAAE,OAAO,SAAS,oBAAoB,QAAQ,WAAW,WAAW,SAAS;AAAA,EACtF,SAAS,OAAgB;AACvB,UAAM,WAAW,SAAS,WAAW,OAAO,OAAO,OAAO,OAAO,IAAI;AACrE,QAAI,UAAU,UAAU;AACtB,aAAO;AAAA,QACL,OAAO,OAAO;AAAA,QACd,SAAS,OAAO;AAAA,QAChB,oBAAoB,OAAO,UAAU,OAAO;AAAA,QAC5C,QAAQ;AAAA,QACR,WAAW,OAAO;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AACA,UAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACpE,UAAM,IAAI,4BAA4B,MAAM;AAAA,EAC9C;AACF;AAOA,SAAS,aAAa,MAAuB;AAC3C,MAAI;AACF,WAAO,KAAK,MAAM,GAAG,aAAa,MAAM,MAAM,CAAC;AAAA,EACjD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,cAAc,SAAgC;AAC5D,QAAM,YAAY,aAAa,KAAK,KAAK,SAAS,gBAAgB,iBAAiB,QAAQ,cAAc,CAAC;AAC1G,MAAI,SAAS,SAAS,KAAK,SAAS,UAAU,OAAO,EAAG,QAAO,UAAU;AACzE,QAAM,WAAW,aAAa,KAAK,KAAK,SAAS,cAAc,CAAC;AAChE,MAAI,CAAC,SAAS,QAAQ,EAAG,QAAO;AAChC,aAAW,SAAS,CAAC,gBAAgB,iBAAiB,GAAY;AAChE,UAAM,OAAO,SAAS,KAAK;AAC3B,QAAI,SAAS,IAAI,KAAK,SAAS,KAAK,YAAY,CAAC,GAAG;AAClD,YAAM,QAAQ,KAAK,YAAY,EAAE,KAAK;AACtC,YAAM,QAAQ,yCAAyC,KAAK,KAAK;AACjE,aAAO,QAAQ,MAAM,CAAC,IAAI;AAAA,IAC5B;AAAA,EACF;AACA,SAAO;AACT;AAIO,SAAS,iBACd,SACA,aACA,MAAyB,QAAQ,KACrB;AACZ,QAAM,cAAc,KAAK,KAAK,SAAS,OAAO,YAAY;AAC1D,MAAI,iBAAuC;AAC3C,MAAI,eAAe,GAAG,WAAW,WAAW,GAAG;AAC7C,QAAI;AACF,uBAAiB,YAAY,aAAa,GAAG;AAAA,IAC/C,QAAQ;AACN,uBAAiB;AAAA,IACnB;AAAA,EACF;AACA,SAAO,EAAE,WAAW,cAAc,OAAO,GAAG,eAAe;AAC7D;AAmCA,SAAS,eAAe,IAAY,SAA+C;AACjF,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,QAAQ,KAAK,CAAC,eAAe,WAAW,OAAO,OAAO,WAAW,QAAQ,kBAAkB,YAAY;AAChH;AAEO,SAAS,gBACd,UACA,SACA,KACkB;AAClB,QAAM,WAAW,IAAI,YAAY,YAAY,IAAI,WAAW,SAAS,MAAM,KAAK,IAAI;AACpF,QAAM,YAAY;AAAA,IAChB,OAAO,QAAQ,SAAS;AAAA,IACxB,GAAI,IAAI,YAAY,EAAE,WAAW,IAAI,UAAU,IAAI,CAAC;AAAA,IACpD,GAAI,QAAQ,WAAW,EAAE,UAAU,QAAQ,SAAS,IAAI,CAAC;AAAA,IACzD,GAAI,QAAQ,QAAQ,EAAE,MAAM,QAAQ,MAAM,IAAI,CAAC;AAAA,EACjD;AACA,QAAM,aAAa,YAAY,UAAU,SAAS;AAClD,QAAM,OAAO,WAAW;AACxB,QAAM,WAAW,QAAQ,aAAa,QAAQ,SAAS;AACvD,QAAM,SAAS,WAAW,aAAa,YAAY,UAAU,EAAE,GAAG,WAAW,qBAAqB,KAAK,CAAC;AACxG,QAAM,OAAO,OAAO;AACpB,QAAM,SAAS,WAAW,IAAI,WAAW,QAAQ;AAAA,IAC/C,CAAC,QAAQ,IAAI,KAAK,MAAM,SAAS,aAAa,SAAS,QAAQ,CAAC,IAAI,KAAK,MAAM,QAAQ,YAAY,IAAI;AAAA,EACzG,EAAE;AACF,QAAM,QAAQ,KAAK,IAAI,kBAAkB,KAAK,IAAI,GAAG,KAAK,MAAM,QAAQ,SAAS,EAAE,CAAC,CAAC;AACrF,QAAM,UAAU,KAAK,MAAM,GAAG,KAAK,EAAE,IAAI,CAAC,QAAQ,aAAa,UAAU,KAAK,MAAM,OAAO,MAAM,CAAC;AAClG,QAAM,UAAU,oBAAI,IAA0B;AAC9C,aAAW,OAAO,KAAK,MAAM,GAAG,KAAK,EAAG,SAAQ,IAAI,IAAI,KAAK,MAAM,SAAS,IAAI,KAAK,KAAK;AAC1F,QAAM,UAAU,OAAO,QAAQ,IAAI,CAAC,UAAU,EAAE,GAAG,MAAM,SAAS,eAAe,KAAK,IAAI,IAAI,cAAc,EAAE,EAAE;AAChH,QAAM,WACJ,YAAY,SAAS,SAAS,OAC1B,gBAAgB,IAAI,SAAS,qCAAqC,SAAS,QAAQ,iBAAiB,uCACpG;AACN,SAAO;AAAA,IACL,MAAM;AAAA,MACJ,eAAe;AAAA,MACf,OAAO,QAAQ,SAAS;AAAA,MACxB;AAAA,MACA,WAAW,IAAI;AAAA,MACf;AAAA,MACA;AAAA,MACA,OAAO,WAAW,QAAQ;AAAA,MAC1B;AAAA,MACA,aAAa,SAAS,MAAM;AAAA,MAC5B,aAAa,SAAS,MAAM;AAAA,MAC5B,eAAe,gBAAgB,QAAQ;AAAA,MACvC,GAAI,OAAO,aAAa,EAAE,YAAY,OAAO,WAAW,IAAI,CAAC;AAAA,IAC/D;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,MAAM,eAAuC,EAAE,UAAU,YAAY,WAAW,aAAa,aAAa,YAAY;AAEtH,SAAS,WAAW,OAAqB,MAA6B;AACpE,QAAM,QAAQ,MAAM,UAAU,eAAe,MAAM,SAAS,aAAa,aAAa;AACtF,MAAI,SAAS,KAAM,QAAO;AAC1B,QAAM,SAAS,SAAS,OAAO,IAAI;AACnC,SAAO,WAAW,aAAa,GAAG,KAAK,SAAM,IAAI,KAAK,GAAG,KAAK,SAAM,MAAM,OAAO,IAAI;AACvF;AAEA,SAAS,YAAY,QAA2C;AAC9D,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,UAAU,OAAO,QAAQ,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,WAAW,OAAO,KAAK,YAAY,EAAE,QAAQ,MAAM,GAAG,CAAC;AACvG,SAAO,QAAQ,SAAS,IAAI,GAAG,OAAO,QAAQ,KAAK,QAAQ,KAAK,IAAI,CAAC,MAAM,OAAO;AACpF;AAEA,SAAS,aAAa,OAA6B;AACjD,SAAO,MAAM,QAAQ,QAAQ,MAAM,QAAQ;AAC7C;AAEA,MAAM,QAAQ,CAAC,YAAoB,QAAQ,MAAM,MAAM;AAQhD,SAAS,kBAAkB,SAAiB,OAA6B;AAC9E,QAAM,MAAM,QAAQ,WAAW,mCAAmC;AAClE,QAAM,YAAY,MAAM,SAAS,YAAY,MAAM,SAAS;AAC5D,QAAM,gBACH,OAAO,aAAc,QAAQ,SAAS,yBAAyB,KAAK,QAAQ,WAAW,sBAAsB;AAChH,QAAM,cAAc,QAAQ,WAAW,WAAW,MAAM,MAAM,SAAS,aAAa,MAAM,SAAS;AACnG,QAAM,YAAY,eAAe,QAAQ,CAAC,MAAM,OAAO,EAAE,SAAS,eAAe;AACjF,QAAM,SAAS,MAAM,SAAS,OAAO;AACrC,QAAM,OAAO,WAAW,UAAa,OAAO,SAAS,MAAM,MAAM,SAAS,QAAQ,cAAc,KAAK;AACrG,QAAM,UAAU,iBAAiB,QAAQ,CAAC,MAAM,OAAO,EAAE,SAAS,iBAAiB;AACnF,SAAO,CAAC,SAAS,GAAI,WAAW,CAAC,eAAe,IAAI,CAAC,GAAI,GAAI,UAAU,CAAC,iBAAiB,IAAI,CAAC,CAAE,EAAE,KAAK,MAAM;AAC/G;AAEA,SAAS,YACP,SACA,OACQ;AACR,MAAI,QAAQ,eAAe,OAAW,QAAO,kBAAkB,QAAQ,SAAS,KAAK;AACrF,QAAM,QAAQ,QAAQ,sBAAsB;AAC5C,SAAO,kBAAkB,MAAM,WAAW,UAAU,OAAO,OAAO,kBAAkB,OAAO,KAAK,CAAC;AACnG;AAEA,SAAS,aAAa,OAAqB,MAA6B;AACtE,QAAM,OAAO,MAAM,OAAO,MAAM,QAAQ,QAAQ,YAAY,EAAE;AAC9D,QAAM,UAAU,eAAe,OAAO,IAAI;AAC1C,QAAM,SAAS,aAAa,MAAM,IAAI,KAAK,MAAM;AACjD,SAAO,GAAG,IAAI,GAAG,UAAU,IAAI,OAAO,KAAK,EAAE,MAAM,WAAW,OAAO,IAAI,CAAC,MAAM,MAAM,SAAM,aAAa,KAAK,CAAC;AACjH;AAGO,SAAS,kBAAkB,MAAwB,SAAgB,oBAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE,GAAa;AACzH,QAAM,EAAE,KAAK,IAAI;AACjB,QAAM,MAAgB,CAAC;AACvB,MAAI,KAAK,SAAU,KAAI,KAAK,KAAK,UAAU,EAAE;AAC7C,MAAI,KAAK,WAAY,KAAI,KAAK,oBAAoB,KAAK,KAAK,2BAA2B,KAAK,UAAU,MAAM,EAAE;AAC9G,MAAI,KAAK,QAAQ,SAAS,GAAG;AAC3B,QAAI,KAAK,+BAA+B;AACxC,UAAM,UAAU,KAAK,IAAI,GAAG,KAAK,QAAQ,IAAI,CAAC,SAAS,KAAK,GAAG,MAAM,CAAC;AACtE,UAAM,aAAa,KAAK,IAAI,GAAG,KAAK,QAAQ,IAAI,CAAC,SAAS,KAAK,MAAM,MAAM,CAAC;AAC5E,eAAW,QAAQ,KAAK,SAAS;AAC/B,YAAM,SAAS,KAAK,UAAU,qBAAqB,KAAK,YAAY,QAAQ,KAAK,UAAW,KAAK,cAAc,KAAK;AACpH,UAAI,KAAK,KAAK,KAAK,GAAG,OAAO,OAAO,CAAC,KAAK,KAAK,MAAM,OAAO,UAAU,CAAC,MAAM,MAAM,EAAE;AAAA,IACvF;AACA,QAAI,KAAK,EAAE;AAAA,EACb;AACA,aAAW,QAAQ,KAAK,SAAS;AAC/B,UAAM,QAAQ,KAAK,QAAQ,IAAI,KAAK,OAAO;AAC3C,QAAI,CAAC,MAAO;AACZ,QAAI,KAAK,aAAa,OAAO,KAAK,IAAI,CAAC;AACvC,QAAI,KAAK,QAAS,KAAI,KAAK,KAAK,KAAK,OAAO,EAAE;AAC9C,UAAM,UAAU;AAAA,MACd,KAAK,UAAU,SAAS,IAAI,YAAY,KAAK,UAAU,KAAK,IAAI,CAAC,KAAK;AAAA,MACtE,YAAY,KAAK,WAAW,IAAI,iBAAiB,YAAY,KAAK,WAAW,CAAC,KAAK;AAAA,IACrF,EAAE,OAAO,CAAC,SAAyB,SAAS,IAAI;AAChD,QAAI,QAAQ,SAAS,EAAG,KAAI,KAAK,KAAK,QAAQ,KAAK,QAAK,CAAC,EAAE;AAC3D,QAAI,KAAK,mBAAmB,KAAK,kBAAkB,SAAS,KAAK,WAAW,YAAY;AACtF,UAAI,KAAK,uBAAuB,KAAK,eAAe,EAAE;AAAA,IACxD;AACA,QAAI,KAAK,cAAc,YAAY,MAAM,KAAK,CAAC,EAAE;AACjD,QAAI,KAAK,WAAW,KAAK,IAAI,EAAE;AAC/B,QAAI,KAAK,EAAE;AAAA,EACb;AACA,QAAM,aACJ,KAAK,SAAS,KAAK,KAAK,SAAS,OAC7B,KAAK,WAAW,IACd,+CAA+C,KAAK,IAAI,uBACxD,GAAG,KAAK,MAAM,gDAAgD,KAAK,IAAI,uBACzE;AACN,MAAI,KAAK,QAAQ,WAAW,KAAK,KAAK,QAAQ,WAAW,GAAG;AAC1D,UAAM,OAAO,KAAK,QAAQ,IAAI,KAAK,KAAK,MAAM;AAC9C,QAAI,KAAK,aAAa,0BAA0B,KAAK,IAAI,YAAY,IAAI,MAAM,sBAAsB,IAAI,GAAG;AAC5G,QAAI,WAAY,KAAI,KAAK,UAAU;AACnC,QAAI,KAAK,uBAAuB,gBAAgB,EAAE;AAAA,EACpD,WAAW,YAAY;AACrB,QAAI,KAAK,UAAU;AAAA,EACrB;AACA,SAAO,IAAI,SAAS,KAAK,IAAI,IAAI,SAAS,CAAC,MAAM,GAAI,KAAI,IAAI;AAC7D,SAAO;AACT;AAsBO,SAAS,cAAc,UAA2B,KAAa,KAAiB,OAA+B;AACpH,QAAM,iBAAiB,SAAS,SAAS,WAAW,CAAC;AACrD,QAAM,UAAU,SAAS,MAAM;AAC/B,QAAM,SAAS,WAAW,SAAS,MAAM,KAAK,GAAG,OAAO;AACxD,QAAM,OAAO,IAAI,YAAY,YAAY,IAAI,WAAW,SAAS,MAAM,KAAK,EAAE,OAAO;AACrF,QAAM,WAAW,WAAW,KAAK,SAAS,cAAc;AACxD,MAAI,SAAS,SAAS,WAAW;AAC/B,UAAM,aAAa,SAAS;AAC5B,WAAO;AAAA,MACL,MAAM;AAAA,QACJ,eAAe;AAAA,QACf;AAAA,QACA,OAAO;AAAA,QACP,YAAY,SAAS;AAAA,QACrB,SAAS;AAAA,QACT,UAAU,gBAAgB,YAAY,IAAI;AAAA,QAC1C,WAAW;AAAA,QACX,MAAM,QAAQ,UAAU,WAAW,IAAI;AAAA,MACzC;AAAA,MACA,SAAS;AAAA,MACT,gBAAgB,eAAe,WAAW,IAAI,IAAI,cAAc;AAAA,MAChE,OAAO;AAAA,IACT;AAAA,EACF;AACA,MAAI,SAAS,SAAS,QAAQ;AAC5B,UAAM,KAAK,aAAa,GAAG,EAAE,KAAK,KAAK,EAAE,YAAY;AACrD,UAAM,SAAS,QAAQ,OAAO,CAACA,WAAUA,OAAM,UAAU,SAAS,EAAE,CAAC;AACrE,QAAI,OAAO,SAAS,GAAG;AACrB,aAAO;AAAA,QACL,MAAM;AAAA,UACJ,eAAe;AAAA,UACf;AAAA,UACA,OAAO;AAAA,UACP,YAAY,SAAS;AAAA,UACrB,SAAS;AAAA,UACT,UAAU,CAAC,sBAAsB,IAAI,OAAO,MAAM,CAAC;AAAA,UACnD,WAAW;AAAA,UACX,MAAM;AAAA,QACR;AAAA,QACA,SAAS;AAAA,QACT,gBAAgB;AAAA,QAChB,OAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,MACL,MAAM,EAAE,eAAe,GAAG,OAAO,qBAAqB,KAAK,SAAS,UAAU,GAAG,YAAY,SAAS,WAAW;AAAA,MACjH,SAAS;AAAA,MACT,gBAAgB;AAAA,MAChB,OAAO;AAAA,IACT;AAAA,EACF;AACA,QAAM,QAAQ,SAAS;AACvB,SAAO;AAAA,IACL,MAAM;AAAA,MACJ,eAAe;AAAA,MACf;AAAA,MACA;AAAA,MACA,YAAY,CAAC;AAAA,MACb,SAAS;AAAA,MACT,UAAU,cAAc,OAAO,OAAO,MAAM,gBAAgB,QAAQ,CAAC;AAAA,MACrE,WAAW;AAAA,MACX,MAAM,QAAQ,UAAU,MAAM,MAAM,EAAE;AAAA,IACxC;AAAA,IACA,SAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,OAAO;AAAA,EACT;AACF;AAEA,SAAS,OAAO,OAAe,KAAa,MAAsB;AAChE,SAAO,GAAG,KAAK,IAAI,UAAU,IAAI,MAAM,IAAI;AAC7C;AAEA,SAAS,YAAY,OAAoC;AACvD,QAAM,QAAQ,MAAM,SAAS;AAC7B,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,QAAQ;AAAA,IACZ,MAAM,UAAU,MAAM,OAAO,SAAS,IAAI,OAAO,MAAM,OAAO,QAAQ,SAAS,QAAQ,IAAI;AAAA,IAC3F,MAAM,eAAe,MAAM,YAAY,SAAS,IAAI,OAAO,MAAM,YAAY,QAAQ,cAAc,aAAa,IAAI;AAAA,IACpH,MAAM,iBAAiB,MAAM,cAAc,SAAS,IAAI,SAAS,OAAO,MAAM,cAAc,QAAQ,SAAS,QAAQ,CAAC,KAAK;AAAA,IAC3H,MAAM,gBAAgB,MAAM,aAAa,SAAS,IAC9C,OAAO,MAAM,aAAa,QAAQ,gBAAgB,eAAe,IACjE;AAAA,EACN,EAAE,OAAO,CAAC,SAAyB,SAAS,IAAI;AAChD,SAAO,MAAM,SAAS,IAAI,MAAM,KAAK,QAAK,IAAI;AAChD;AAEO,SAAS,gBAAgB,MAAsB,UAAqC;AACzF,QAAM,EAAE,KAAK,IAAI;AACjB,MAAI,WAAW,KAAM,QAAO,CAAC,KAAK,KAAK;AACvC,QAAM,MAAgB,CAAC;AACvB,MAAI,KAAK,SAAS;AAChB,UAAM,aAAa,KAAK;AACxB,QAAI,KAAK,GAAG,WAAW,EAAE,qCAAqC,WAAW,KAAK,GAAG;AACjF,QAAI,WAAW,YAAa,KAAI,KAAK,KAAK,WAAW,WAAW,EAAE;AAClE,UAAM,aAAa,KAAK,mBAAmB,QAAQ,OAAO,kBAAkB,YAAY,KAAK,IAAI;AACjG,QAAI,KAAK,eAAgB,KAAI,KAAK,oBAAoB;AAAA,aAC7C,WAAY,KAAI,KAAK,KAAK,UAAU,EAAE;AAAA,QAC1C,KAAI,KAAK,aAAa,WAAW,WAAW,qBAAqB,WAAW,EAAE,CAAC,EAAE;AACtF,QAAI,KAAK,WAAW,SAAS,EAAG,KAAI,KAAK,uCAAuC,KAAK,WAAW,KAAK,IAAI,CAAC,EAAE;AAC5G,QAAI,KAAK,KAAM,KAAI,KAAK,WAAW,KAAK,IAAI,EAAE;AAC9C,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,KAAK;AACnB,MAAI,CAAC,OAAO;AACV,QAAI,KAAK,GAAG,KAAK,QAAQ;AACzB,QAAI,KAAK,WAAW,SAAS,EAAG,KAAI,KAAK,eAAe,KAAK,WAAW,KAAK,IAAI,CAAC,EAAE;AACpF,WAAO;AAAA,EACT;AACA,QAAM,OAAO,KAAK;AAClB,MAAI,KAAK,aAAa,OAAO,IAAI,CAAC;AAClC,MAAI,KAAK,KAAK,MAAM,KAAK,KAAK,MAAM,OAAO,EAAE;AAC7C,MAAI,MAAM,UAAW,KAAI,KAAK,KAAK,MAAM,SAAS,EAAE;AACpD,QAAM,QAAQ,SAAS,MAAM,MAAM,IAAI,CAAC,SAAS,GAAG,KAAK,GAAG,IAAI,SAAS,OAAO,KAAK,GAAG,CAAC,EAAE;AAC3F,MAAI,MAAM,SAAS,EAAG,KAAI,KAAK,YAAY,MAAM,KAAK,QAAK,CAAC,EAAE;AAC9D,QAAM,SAAS,OAAQ,MAAM,YAAY,IAAI,KAAK,OAAQ;AAC1D,MAAI,UAAU,MAAM;AAClB,QAAI,KAAK,qBAAqB,IAAI,KAAK,OAAO,QAAQ,EAAE;AACxD,eAAW,UAAU,OAAO,QAAQ,MAAM,GAAG,CAAC,EAAG,KAAI,KAAK,OAAO,OAAO,IAAI,KAAK,OAAO,IAAI,EAAE;AAAA,EAChG;AACA,QAAM,OAAO,YAAY,KAAK;AAC9B,MAAI,KAAM,KAAI,KAAK,WAAW,IAAI,EAAE;AACpC,QAAM,QAAQ,MAAM,SAAS;AAC7B,QAAM,WAAW,CAAC,GAAI,OAAO,OAAO,CAAC,GAAI,IAAI,OAAO,eAAe,CAAC,GAAG,IAAI,CAAC,eAAe,WAAW,GAAG,CAAC;AAC1G,MAAI,SAAS,SAAS,EAAG,KAAI,KAAK,qBAAqB,SAAS,MAAM,GAAG,EAAE,EAAE,KAAK,IAAI,CAAC,EAAE;AACzF,MAAI,OAAO,SAAS,MAAM,MAAM,SAAS,EAAG,KAAI,KAAK,eAAe,MAAM,MAAM,MAAM,GAAG,EAAE,EAAE,KAAK,IAAI,CAAC,EAAE;AACzG,QAAM,UAAU,cAAc,OAAO,MAAM,WAAW,SAAS,MAAM,KAAK,GAAG,OAAO,MAAM,WAAW,QAAQ,CAAC;AAC9G,MAAI,KAAK,cAAc,YAAY,SAAS,KAAK,CAAC,EAAE;AACpD,aAAW,WAAW,KAAK,SAAU,KAAI,KAAK,cAAc,OAAO,EAAE;AACrE,MAAI,KAAK,mBAAmB,KAAK,QAAQ,MAAM,MAAM,EAAE,EAAE;AACzD,MAAI,KAAK,WAAW,QAAQ,UAAU,MAAM,MAAM,IAAI,CAAC,EAAE;AACzD,SAAO;AACT;AAYA,MAAM,eACJ;AACF,MAAM,aAAa;AAEZ,MAAM,+BACX;AAIF,eAAe,eAAe,MAAmD;AAC/E,QAAM,MAAM,KAAK,OAAO,QAAQ;AAChC,QAAM,UAAU,KAAK,WAAW,eAAe,EAAE,UAAU;AAC3D,QAAM,SAAS,KAAK,WAAW,CAAC,SAAiB,QAAQ,MAAM,IAAI;AACnE,QAAM,MAAM,IAAI,sBAAsB,KAAK,KAAK;AAChD,QAAM,aAAa,IAAI,wBAAwB,KAAK,KAAK;AACzD,QAAM,SAAS,MAAM,UAAU;AAAA,IAC7B;AAAA,IACA;AAAA,IACA,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,IACnC,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,UAAU,IAAI,CAAC;AAAA,IACtD,GAAI,KAAK,MAAM,EAAE,KAAK,KAAK,IAAI,IAAI,CAAC;AAAA,EACtC,CAAC;AACD,MAAI,OAAO,WAAW,cAAe,QAAO,kCAAkC,OAAO,SAAS,GAAG;AACjG,MAAI,OAAO,uBAAuB,MAAM;AACtC,WAAO,4BAA4B;AACnC,WAAO,WAAW,OAAO,kBAAkB,EAAE;AAAA,EAC/C;AACA,QAAM,MAAM,iBAAiB,SAAS,KAAK,sBAAsB,MAAM,GAAG;AAC1E,SAAO;AAAA,IACL,UAAU,OAAO;AAAA,IACjB;AAAA,IACA,oBAAoB,OAAO,uBAAuB;AAAA,EACpD;AACF;AAEA,SAAS,gBAAgB,OAAgB,QAAwC;AAC/E,MAAI,iBAAiB,6BAA6B;AAChD,WAAO,gCAAgC;AACvC,WAAO,WAAW,MAAM,MAAM,EAAE;AAChC,WAAO;AAAA,EACT;AACA,QAAM;AACR;AAEA,eAAsB,gBAAgB,MAAgB,OAAyB,CAAC,GAAoB;AAClG,QAAM,SAAS,KAAK,WAAW,CAAC,SAAiB,QAAQ,IAAI,IAAI;AACjE,QAAM,SAAS,KAAK,WAAW,CAAC,SAAiB,QAAQ,MAAM,IAAI;AACnE,QAAM,SAAS,sBAAsB,IAAI;AACzC,MAAI,CAAC,OAAO,SAAS,CAAC,OAAO,YAAY,CAAC,OAAO,OAAO;AACtD,WAAO,YAAY;AACnB,WAAO;AAAA,EACT;AACA,MAAI;AACJ,MAAI;AACF,aAAS,MAAM,eAAe,IAAI;AAAA,EACpC,SAAS,OAAgB;AACvB,WAAO,gBAAgB,OAAO,MAAM;AAAA,EACtC;AACA,QAAM,OAAO;AAAA,IACX,OAAO;AAAA,IACP,EAAE,OAAO,OAAO,OAAO,UAAU,OAAO,UAAU,OAAO,OAAO,OAAO,UAAU,OAAO,UAAU,OAAO,OAAO,MAAM;AAAA,IACtH,OAAO;AAAA,EACT;AACA,MAAI,OAAO,mBAAoB,MAAK,KAAK,qBAAqB;AAC9D,QAAM,QAAQ,IAAI,KAAK,KAAK,MAAM,KAAK,IAAI,IAAI,KAAK,IAAI,CAAC,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AACpF,MAAI,OAAO,KAAM,QAAO,KAAK,UAAU,KAAK,MAAM,MAAM,CAAC,CAAC;AAAA,MACrD,YAAW,QAAQ,kBAAkB,MAAM,KAAK,EAAG,QAAO,IAAI;AACnE,SAAO;AACT;AAEA,eAAsB,cAAc,MAAgB,OAAyB,CAAC,GAAoB;AAChG,QAAM,SAAS,KAAK,WAAW,CAAC,SAAiB,QAAQ,IAAI,IAAI;AACjE,QAAM,SAAS,KAAK,WAAW,CAAC,SAAiB,QAAQ,MAAM,IAAI;AACnE,QAAM,SAAS,oBAAoB,IAAI;AACvC,MAAI,CAAC,OAAO,KAAK;AACf,WAAO,UAAU;AACjB,WAAO;AAAA,EACT;AACA,MAAI;AACJ,MAAI;AACF,aAAS,MAAM,eAAe,IAAI;AAAA,EACpC,SAAS,OAAgB;AACvB,WAAO,gBAAgB,OAAO,MAAM;AAAA,EACtC;AACA,QAAM,QAAQ,IAAI,KAAK,KAAK,MAAM,KAAK,IAAI,IAAI,KAAK,IAAI,CAAC,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AACpF,QAAM,OAAO,cAAc,OAAO,UAAU,OAAO,KAAK,OAAO,KAAK,KAAK;AACzE,MAAI,OAAO,KAAM,QAAO,KAAK,UAAU,KAAK,MAAM,MAAM,CAAC,CAAC;AAAA,WACjD,KAAK,MAAO,YAAW,QAAQ,gBAAgB,MAAM,OAAO,QAAQ,EAAG,QAAO,IAAI;AAAA,MACtF,YAAW,QAAQ,gBAAgB,MAAM,OAAO,QAAQ,EAAG,QAAO,IAAI;AAC3E,SAAO,KAAK,QAAQ,IAAI;AAC1B;",
4
+ "sourcesContent": ["import fs from 'node:fs'\nimport path from 'node:path'\nimport type { ModuleEntry } from './modules-config'\nimport { createResolver } from './resolver'\nimport {\n builtInEnableCommand,\n builtInPresetText,\n builtInWarnings,\n cardUrl,\n CORE_PACKAGE,\n createCatalogSearcher,\n ENTERPRISE_ENABLE_STEP,\n entryWarnings,\n installFields,\n installVersion,\n latestLine,\n newAppInstallText,\n peerLookup,\n rankModules,\n receiptSourceOf,\n resolveLine,\n resolveRef,\n SEARCH_MAX_LIMIT,\n sharedModuleIdWarning,\n statusOn,\n stripVersion,\n toModuleCard,\n unknownModuleMessage,\n type AgentPolicy,\n type BuiltInCard,\n type BuiltInIndex,\n type BuiltInModule,\n type CatalogEntry,\n type CatalogIndex,\n type CatalogSearcher,\n type ModuleCard,\n type ReceiptSource,\n} from './module-search-index'\nimport { parseModuleInfoArgs, parseModuleSearchArgs } from './module-search-args'\n\nexport const DEFAULT_MODULES_INDEX_URL = 'https://modules.openmercato.com/modules.json'\nexport const MODULES_SITE_URL = 'https://modules.openmercato.com'\nexport const MODULE_INDEX_MAX_BYTES = 10 * 1024 * 1024\nexport const MODULE_INDEX_TIMEOUT_MS = 4000\nexport const MODULE_INDEX_TTL_MS = 10 * 60 * 1000\nexport const MODULE_INDEX_CACHE_FILE = path.join('.mercato', 'cache', 'modules-index.json')\nexport const MODULE_INDEX_UNAVAILABLE_EXIT_CODE = 2\nexport const MODULE_INDEX_UNAVAILABLE_MESSAGE =\n 'Module index unavailable. Check your connection or set OM_MODULES_INDEX_URL.'\n\nexport type FetchResponseLike = {\n status: number\n ok: boolean\n headers: { get(name: string): string | null }\n body?: ReadableStream<Uint8Array> | null\n json(): Promise<unknown>\n}\n\nexport type FetchLike = (\n input: string,\n init?: { headers?: Record<string, string>; signal?: AbortSignal; redirect?: 'manual' },\n) => Promise<FetchResponseLike>\n\nexport type LoadedModuleIndex = {\n index: CatalogIndex\n builtIn: BuiltInIndex | null\n /** Why the built-in module list is missing (`builtIn` is null), or null when it loaded. */\n builtInUnavailable: string | null\n source: 'network' | 'cache' | 'stale-cache'\n fetchedAt: string\n searcher: CatalogSearcher\n}\n\nexport type LoadIndexOptions = {\n appRoot: string\n url?: string\n builtInUrl?: string\n timeoutMs?: number\n ttlMs?: number\n fetchImpl?: FetchLike\n now?: () => number\n}\n\ntype CacheFile = {\n cacheVersion: 1\n url: string\n builtInUrl: string\n fetchedAt: string\n etag: string | null\n builtInEtag: string | null\n index: CatalogIndex\n builtIn: BuiltInIndex | null\n}\n\nexport class ModuleIndexUnavailableError extends Error {\n readonly detail: string\n\n constructor(detail: string) {\n super(`${MODULE_INDEX_UNAVAILABLE_MESSAGE} (${detail})`)\n this.name = 'ModuleIndexUnavailableError'\n this.detail = detail\n }\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\nconst isString = (value: unknown): value is string => typeof value === 'string'\nconst isStringArray = (value: unknown): value is string[] => Array.isArray(value) && value.every(isString)\nconst isNullableString = (value: unknown): value is string | null => value === null || isString(value)\nconst isArrayOf = (value: unknown, item: (value: unknown) => boolean): boolean => Array.isArray(value) && value.every(item)\nconst isRecordOf = (value: unknown, item: (value: unknown) => boolean): boolean =>\n isRecord(value) && Object.values(value).every(item)\nconst isAbsentOr = (value: unknown, check: (value: unknown) => boolean): boolean => value === undefined || check(value)\n\nconst ANATOMY_LISTS = ['aclFeatures', 'eventsEmitted', 'eventsConsumed', 'env', 'hosts', 'tables', 'publicRoutes'] as const\n\n// The anatomy lists search indexes and `module info` prints; a module without anatomy has null.\nfunction isAnatomy(value: unknown): boolean {\n if (value === null || value === undefined) return true\n if (!isRecord(value)) return false\n const lists = value.lists\n if (lists === undefined) return true\n return (\n isRecord(lists) &&\n ANATOMY_LISTS.every((key) => isAbsentOr(lists[key], isStringArray)) &&\n isAbsentOr(lists.credentials, (credentials) =>\n isArrayOf(credentials, (credential) => isRecord(credential) && isString(credential.key)),\n )\n )\n}\n\nfunction isAgentPolicy(value: unknown): boolean {\n return (\n isRecord(value) &&\n isString(value.decision) &&\n isArrayOf(value.reasons, (reason) => isRecord(reason) && isString(reason.code) && isString(reason.text))\n )\n}\n\nfunction isBuiltFor(value: unknown): boolean {\n return (\n isRecord(value) &&\n isString(value.kind) &&\n isNullableString(value.range) &&\n isStringArray(value.lines) &&\n isAbsentOr(value.ranges, (ranges) => isRecordOf(ranges, isString))\n )\n}\n\nfunction isCompat(value: unknown): boolean {\n return (\n isRecord(value) &&\n isRecordOf(\n value.lines,\n (cell) =>\n isRecord(cell) &&\n isString(cell.status) &&\n isAbsentOr(cell.at, isNullableString) &&\n isAbsentOr(cell.moduleVersion, isNullableString),\n ) &&\n isStringArray(value.verifiedOn) &&\n isAbsentOr(value.builtFor, isBuiltFor) &&\n isAbsentOr(value.inPresets, isStringArray) &&\n isAbsentOr(value.inEmptyPreset, isStringArray) &&\n isAbsentOr(value.ships, (ships) => isRecordOf(ships, isString)) &&\n isAbsentOr(value.isolation, (isolation) =>\n isRecordOf(isolation, (cell) => isRecord(cell) && isString(cell.status) && isNullableString(cell.moduleVersion)),\n )\n )\n}\n\nfunction isSignals(value: unknown): boolean {\n if (value === null || value === undefined) return true\n return (\n isRecord(value) &&\n isAbsentOr(value.supplyChain, (item) => isRecord(item) && isString(item.status) && isStringArray(item.reasons)) &&\n isAbsentOr(value.hygiene, (item) => isRecord(item) && isString(item.status) && isStringArray(item.failing)) &&\n isAbsentOr(value.anatomy, isString)\n )\n}\n\n// Every field search, `module info` and their output read, nested ones too: an entry that passes is never the reason\n// a command throws, so a malformed index is refused before it can replace a working cached copy.\nfunction isCatalogEntry(value: unknown): value is CatalogEntry {\n if (!isRecord(value)) return false\n const version = value.version\n const install = value.install\n const links = value.links\n const license = value.license\n return (\n isString(value.locator) &&\n isString(value.path) &&\n (value.npm === null || isString(value.npm)) &&\n isString(value.title) &&\n isString(value.summary) &&\n isString(value.whenToUse) &&\n isString(value.type) &&\n isString(value.kind) &&\n isString(value.category) &&\n isStringArray(value.tags) &&\n isArrayOf(value.providers, (provider) => isRecord(provider) && isString(provider.name) && isString(provider.label)) &&\n isStringArray(value.moduleIds) &&\n isAbsentOr(value.requires, (requires) =>\n isArrayOf(\n requires,\n (requirement) => isRecord(requirement) && isString(requirement.raw) && isAbsentOr(requirement.resolvesTo, isNullableString),\n ),\n ) &&\n isStringArray(value.renamedFrom) &&\n isString(value.status) &&\n typeof value.listed === 'boolean' &&\n typeof value.featured === 'boolean' &&\n isNullableString(value.tier) &&\n isRecord(license) &&\n isNullableString(license.spdx) &&\n isString(license.kind) &&\n isRecord(version) &&\n isNullableString(version.latest) &&\n isNullableString(version.preview) &&\n isNullableString(version.installableFrom) &&\n isNullableString(version.gitRef) &&\n isRecordOf(version.recommended, isNullableString) &&\n isCompat(value.compat) &&\n isAnatomy(value.anatomy) &&\n isSignals(value.signals) &&\n isRecordOf(value.agentPolicy, isAgentPolicy) &&\n isRecord(install) &&\n isString(install.cli) &&\n isAbsentOr(install.moduleFlag, isNullableString) &&\n isRecord(links) &&\n isString(links.page) &&\n isString(links.md)\n )\n}\n\nexport function parseCatalogIndex(value: unknown): CatalogIndex | null {\n if (!isRecord(value) || value.schemaVersion !== 1) return null\n if (!isString(value.generatedAt) || !isString(value.dataVersion) || !isString(value.site)) return null\n if (!Array.isArray(value.lines) || !Array.isArray(value.entries)) return null\n if (!isAbsentOr(value.dataAsOf, isRecord)) return null\n if (!value.lines.every((line) => isRecord(line) && isString(line.key) && isString(line.version) && isString(line.source))) {\n return null\n }\n if (!value.entries.every(isCatalogEntry)) return null\n const lines = value.lines.filter(\n (line): line is CatalogIndex['lines'][number] =>\n line.source === 'latest' || line.source === 'previous' || line.source === 'develop',\n )\n return { ...(value as CatalogIndex), lines }\n}\n\nexport function parseBuiltInIndex(value: unknown): BuiltInIndex | null {\n if (!isRecord(value) || value.schemaVersion !== 1 || !isString(value.coreVersion) || !Array.isArray(value.modules)) {\n return null\n }\n const valid = value.modules.every(\n (coreModule) =>\n isRecord(coreModule) &&\n isString(coreModule.id) &&\n isString(coreModule.title) &&\n isString(coreModule.description) &&\n isString(coreModule.category) &&\n isString(coreModule.install) &&\n isString(coreModule.page) &&\n isAbsentOr(coreModule.requires, isStringArray) &&\n isAbsentOr(coreModule.inPresets, isStringArray) &&\n isAbsentOr(coreModule.inEmptyPreset, isStringArray) &&\n isAnatomy(coreModule.anatomy),\n )\n return valid ? (value as BuiltInIndex) : null\n}\n\nfunction parseCacheFile(value: unknown): CacheFile | null {\n if (!isRecord(value) || value.cacheVersion !== 1) return null\n if (!isString(value.url) || !isString(value.builtInUrl) || !isString(value.fetchedAt)) return null\n const index = parseCatalogIndex(value.index)\n if (!index) return null\n const builtIn = value.builtIn === null ? null : parseBuiltInIndex(value.builtIn)\n return {\n cacheVersion: 1,\n url: value.url,\n builtInUrl: value.builtInUrl,\n fetchedAt: value.fetchedAt,\n etag: isString(value.etag) ? value.etag : null,\n builtInEtag: isString(value.builtInEtag) ? value.builtInEtag : null,\n index,\n builtIn,\n }\n}\n\nexport function defaultBuiltInUrl(indexUrl: string): string {\n const url = new URL(indexUrl)\n url.search = ''\n url.hash = ''\n url.pathname = url.pathname.replace(/[^/]*$/, 'built-in.json')\n return url.toString()\n}\n\nfunction readCache(file: string): CacheFile | null {\n try {\n return parseCacheFile(JSON.parse(fs.readFileSync(file, 'utf8')))\n } catch {\n return null\n }\n}\n\nfunction writeCache(file: string, cache: CacheFile): void {\n try {\n fs.mkdirSync(path.dirname(file), { recursive: true })\n const temporary = `${file}.${process.pid}.tmp`\n fs.writeFileSync(temporary, JSON.stringify(cache))\n fs.renameSync(temporary, file)\n } catch {\n return\n }\n}\n\nfunction validateIndexUrl(value: string): void {\n const url = new URL(value)\n const local = url.hostname === 'localhost' || url.hostname === '127.0.0.1' || url.hostname === '[::1]'\n if (url.protocol !== 'https:' && !(url.protocol === 'http:' && local)) {\n throw new Error('[internal] Module index URL must use HTTPS except on loopback hosts')\n }\n}\n\nasync function fetchOnce(\n fetchImpl: FetchLike,\n url: string,\n etag: string | null,\n timeoutMs: number,\n): Promise<FetchResponseLike> {\n const headers: Record<string, string> = { accept: 'application/json' }\n if (etag) headers['if-none-match'] = etag\n const signal = AbortSignal.timeout(timeoutMs)\n let target = url\n for (let redirects = 0; redirects <= 5; redirects += 1) {\n validateIndexUrl(target)\n const response = await fetchImpl(target, { headers, signal, redirect: 'manual' })\n if (![301, 302, 303, 307, 308].includes(response.status)) return response\n await response.body?.cancel()\n const location = response.headers.get('location')\n if (!location || redirects === 5) throw new Error('[internal] Invalid or excessive module index redirects')\n target = new URL(location, target).toString()\n }\n throw new Error('[internal] Excessive module index redirects')\n}\n\nasync function readBoundedJson(response: FetchResponseLike): Promise<unknown> {\n const tooLarge = () => new Error(`[internal] Module index exceeds ${MODULE_INDEX_MAX_BYTES} bytes`)\n const length = Number(response.headers.get('content-length'))\n if (length > MODULE_INDEX_MAX_BYTES) {\n await response.body?.cancel()\n throw tooLarge()\n }\n if (!response.body) {\n const value = await response.json()\n if (Buffer.byteLength(JSON.stringify(value) ?? '') > MODULE_INDEX_MAX_BYTES) throw tooLarge()\n return value\n }\n const reader = response.body.getReader()\n const chunks: Uint8Array[] = []\n let bytes = 0\n try {\n while (true) {\n const chunk = await reader.read()\n if (chunk.done) break\n bytes += chunk.value.byteLength\n if (bytes > MODULE_INDEX_MAX_BYTES) {\n await reader.cancel()\n throw tooLarge()\n }\n chunks.push(chunk.value)\n }\n return JSON.parse(Buffer.concat(chunks).toString('utf8'))\n } finally {\n reader.releaseLock()\n }\n}\n\nasync function fetchWithRetry(\n fetchImpl: FetchLike,\n url: string,\n etag: string | null,\n timeoutMs: number,\n): Promise<FetchResponseLike> {\n let lastError: unknown = null\n for (let attempt = 0; attempt < 2; attempt += 1) {\n try {\n const response = await fetchOnce(fetchImpl, url, etag, timeoutMs)\n if (response.status >= 500) {\n await response.body?.cancel()\n lastError = new Error(`${url} answered ${response.status}`)\n continue\n }\n return response\n } catch (error: unknown) {\n lastError = error\n }\n }\n throw lastError instanceof Error ? lastError : new Error(String(lastError))\n}\n\nasync function fetchJson<T>(\n fetchImpl: FetchLike,\n url: string,\n etag: string | null,\n timeoutMs: number,\n parse: (value: unknown) => T | null,\n): Promise<{ notModified: true } | { notModified: false; value: T; etag: string | null }> {\n const response = await fetchWithRetry(fetchImpl, url, etag, timeoutMs)\n if (response.status === 304) return { notModified: true }\n if (!response.ok) throw new Error(`${url} answered ${response.status}`)\n const value = parse(await readBoundedJson(response))\n if (value === null) throw new Error(`${url} is not a module index this CLI understands`)\n return { notModified: false, value, etag: response.headers.get('etag') }\n}\n\nfunction searcherOf(index: CatalogIndex, builtIn: BuiltInIndex | null): CatalogSearcher | null {\n try {\n return createCatalogSearcher(index, builtIn)\n } catch {\n return null\n }\n}\n\nexport async function loadIndex(options: LoadIndexOptions): Promise<LoadedModuleIndex> {\n const url = options.url ?? DEFAULT_MODULES_INDEX_URL\n let builtInUrl: string\n try {\n validateIndexUrl(url)\n builtInUrl = options.builtInUrl ?? defaultBuiltInUrl(url)\n validateIndexUrl(builtInUrl)\n } catch (error: unknown) {\n throw new ModuleIndexUnavailableError(error instanceof Error ? error.message : String(error))\n }\n const timeoutMs = options.timeoutMs ?? MODULE_INDEX_TIMEOUT_MS\n const ttlMs = options.ttlMs ?? MODULE_INDEX_TTL_MS\n const fetchImpl: FetchLike = options.fetchImpl ?? ((input, init) => fetch(input, init))\n const now = options.now ?? (() => Date.now())\n const cacheFile = path.join(options.appRoot, MODULE_INDEX_CACHE_FILE)\n const cached = readCache(cacheFile)\n let usable = cached && cached.url === url && cached.builtInUrl === builtInUrl ? cached : null\n\n // a copy without the built-in list is not fresh: the next run asks for built-in.json again\n if (usable && usable.builtIn && now() - Date.parse(usable.fetchedAt) < ttlMs) {\n const searcher = searcherOf(usable.index, usable.builtIn)\n if (searcher) {\n return { index: usable.index, builtIn: usable.builtIn, builtInUnavailable: null, source: 'cache', fetchedAt: usable.fetchedAt, searcher }\n }\n // a copy this CLI cannot search is no copy: download a new one\n usable = null\n }\n\n try {\n const indexResult = await fetchJson(fetchImpl, url, usable?.etag ?? null, timeoutMs, parseCatalogIndex)\n let index: CatalogIndex\n let etag: string | null\n if (indexResult.notModified) {\n if (!usable) throw new Error(`${url} answered 304 without a cached copy`)\n index = usable.index\n etag = usable.etag\n } else {\n index = indexResult.value\n etag = indexResult.etag\n }\n let builtIn = usable?.builtIn ?? null\n let builtInEtag = usable?.builtInEtag ?? null\n let builtInUnavailable: string | null = null\n try {\n const builtInResult = await fetchJson(\n fetchImpl,\n builtInUrl,\n usable?.builtIn ? builtInEtag : null,\n timeoutMs,\n parseBuiltInIndex,\n )\n if (!builtInResult.notModified) {\n builtIn = builtInResult.value\n builtInEtag = builtInResult.etag\n }\n } catch (error: unknown) {\n builtIn = usable?.builtIn ?? null\n if (!builtIn) builtInUnavailable = error instanceof Error ? error.message : String(error)\n }\n // built before the copy is saved, so an index the CLI cannot search never replaces one it can\n const searcher = createCatalogSearcher(index, builtIn)\n const fetchedAt = new Date(now()).toISOString()\n writeCache(cacheFile, {\n cacheVersion: 1,\n url,\n builtInUrl,\n fetchedAt,\n etag,\n builtInEtag,\n index,\n builtIn,\n })\n return { index, builtIn, builtInUnavailable, source: 'network', fetchedAt, searcher }\n } catch (error: unknown) {\n const searcher = usable ? searcherOf(usable.index, usable.builtIn) : null\n if (usable && searcher) {\n return {\n index: usable.index,\n builtIn: usable.builtIn,\n builtInUnavailable: usable.builtIn ? null : 'the cached copy has no built-in module list',\n source: 'stale-cache',\n fetchedAt: usable.fetchedAt,\n searcher,\n }\n }\n const detail = error instanceof Error ? error.message : String(error)\n throw new ModuleIndexUnavailableError(detail)\n }\n}\n\nexport type AppContext = {\n omVersion: string | null\n enabledModules: ModuleEntry[] | null\n}\n\nfunction readJsonFile(file: string): unknown {\n try {\n return JSON.parse(fs.readFileSync(file, 'utf8'))\n } catch {\n return null\n }\n}\n\nexport function detectAppLine(appRoot: string): string | null {\n const installed = readJsonFile(path.join(appRoot, 'node_modules', '@open-mercato', 'core', 'package.json'))\n if (isRecord(installed) && isString(installed.version)) return installed.version\n const manifest = readJsonFile(path.join(appRoot, 'package.json'))\n if (!isRecord(manifest)) return null\n for (const field of ['dependencies', 'devDependencies'] as const) {\n const deps = manifest[field]\n if (isRecord(deps) && isString(deps[CORE_PACKAGE])) {\n const range = deps[CORE_PACKAGE].trim()\n const match = /\\d+\\.\\d+(?:\\.\\d+)?(?:-[0-9A-Za-z.-]+)?/.exec(range)\n return match ? match[0] : null\n }\n }\n return null\n}\n\nexport type ReadEnabledModules = (modulesPath: string, env: NodeJS.ProcessEnv) => ModuleEntry[]\n\nexport function detectAppContext(\n appRoot: string,\n readEnabled: ReadEnabledModules | null,\n env: NodeJS.ProcessEnv = process.env,\n): AppContext {\n const modulesPath = path.join(appRoot, 'src', 'modules.ts')\n let enabledModules: ModuleEntry[] | null = null\n if (readEnabled && fs.existsSync(modulesPath)) {\n try {\n enabledModules = readEnabled(modulesPath, env)\n } catch {\n enabledModules = null\n }\n }\n return { omVersion: detectAppLine(appRoot), enabledModules }\n}\n\nexport type ModuleSearchJson = {\n schemaVersion: 1\n query: string\n line: string | null\n omVersion: string | null\n results: ModuleCard[]\n builtIn: BuiltInCard[]\n /** Present (true) when the built-in module list could not be loaded, so `builtIn` may miss modules core has. */\n builtInUnavailable?: true\n total: number\n hidden: number\n dataVersion: string\n generatedAt: string\n receiptSource: ReceiptSource | null\n /** Set when nothing matched the query and the results are for this spelling of it (`stirpe` -> `stripe`). */\n didYouMean?: string\n}\n\nexport type ModuleSearchView = {\n json: ModuleSearchJson\n entries: Map<string, CatalogEntry>\n lineNote: string | null\n allLines: boolean\n}\n\nexport type ModuleSearchRequest = {\n query: string | null\n category?: string | null\n kinds?: ('module' | 'provider' | 'library' | 'tool' | 'built-in')[] | null\n allLines?: boolean\n limit?: number\n}\n\nfunction builtInEnabled(id: string, enabled: ModuleEntry[] | null): boolean | null {\n if (!enabled) return null\n return enabled.some((coreModule) => coreModule.id === id && (coreModule.from ?? CORE_PACKAGE) === CORE_PACKAGE)\n}\n\nexport function buildSearchView(\n searcher: CatalogSearcher,\n request: ModuleSearchRequest,\n app: AppContext,\n): ModuleSearchView {\n const resolved = app.omVersion ? resolveLine(app.omVersion, searcher.index.lines) : null\n const rankInput = {\n query: request.query ?? '',\n ...(app.omVersion ? { omVersion: app.omVersion } : {}),\n ...(request.category ? { category: request.category } : {}),\n ...(request.kinds ? { kind: request.kinds } : {}),\n }\n const unfiltered = rankModules(searcher, rankInput)\n const line = unfiltered.line\n const allLines = request.allLines === true || line === null\n const ranked = allLines ? unfiltered : rankModules(searcher, { ...rankInput, verifiedModulesOnly: true })\n const kept = ranked.modules\n const hidden = allLines ? 0 : unfiltered.modules.filter(\n (hit) => hit.meta.entry.kind === 'module' && (line === null || !hit.meta.entry.version.recommended[line]),\n ).length\n const limit = Math.min(SEARCH_MAX_LIMIT, Math.max(1, Math.floor(request.limit ?? 10)))\n const results = kept.slice(0, limit).map((hit) => toModuleCard(searcher, hit, line, ranked.latest))\n const entries = new Map<string, CatalogEntry>()\n for (const hit of kept.slice(0, limit)) entries.set(hit.meta.entry.locator, hit.meta.entry)\n const builtIn = ranked.builtIn.map((card) => ({ ...card, enabled: builtInEnabled(card.id, app.enabledModules) }))\n const lineNote =\n resolved && resolved.line === null\n ? `Open Mercato ${app.omVersion} is not a line the catalog tests (${resolved.note ?? 'unknown version'}). Showing modules for every line.`\n : null\n return {\n json: {\n schemaVersion: 1,\n query: request.query ?? '',\n line,\n omVersion: app.omVersion,\n results,\n builtIn,\n total: unfiltered.modules.length,\n hidden,\n dataVersion: searcher.index.dataVersion,\n generatedAt: searcher.index.generatedAt,\n receiptSource: receiptSourceOf(searcher),\n ...(ranked.didYouMean ? { didYouMean: ranked.didYouMean } : {}),\n },\n entries,\n lineNote,\n allLines,\n }\n}\n\nconst ORIGIN_LABEL: Record<string, string> = { official: 'official', community: 'community', '3rd-party': '3rd-party' }\n\nfunction levelLabel(entry: CatalogEntry, line: string | null): string {\n const level = entry.quality ? 'OM QUALITY' : entry.tier === 'verified' ? 'VERIFIED' : 'LISTED'\n if (line === null) return level\n const status = statusOn(entry, line)\n return status === 'verified' ? `${level} \u00B7 ${line}` : `${level} \u00B7 ${status} on ${line}`\n}\n\nfunction policyLabel(policy: AgentPolicy | null): string | null {\n if (!policy) return null\n const reasons = policy.reasons.slice(0, 2).map((reason) => reason.code.toLowerCase().replace(/_/g, '-'))\n return reasons.length > 0 ? `${policy.decision} (${reasons.join(', ')})` : policy.decision\n}\n\nfunction licenseLabel(entry: CatalogEntry): string {\n return entry.license.spdx ?? entry.license.kind\n}\n\nconst steps = (command: string) => command.split(' && ')\n\n/**\n * The install line a person copies: the command, then `yarn generate` after a `yarn add` of a library or a provider\n * and after `npx create-mercato-module install` (`yarn mercato module add` runs it itself), then `yarn db:migrate`\n * after a module install (also the enterprise step) that adds tables or migrations or whose anatomy is unknown.\n * `--json` keeps the bare command (tools@1).\n */\nexport function withFollowUpSteps(command: string, entry: CatalogEntry): string {\n const cmm = command.startsWith('npx create-mercato-module install')\n const ofModules = entry.kind === 'module' || entry.kind === 'provider'\n const moduleInstall =\n (cmm && ofModules) || command.includes('yarn mercato module add') || command.startsWith(ENTERPRISE_ENABLE_STEP)\n const addsPackage = command.startsWith('yarn add ') && (entry.kind === 'library' || entry.kind === 'provider')\n const generate = (addsPackage || cmm) && !steps(command).includes('yarn generate')\n const tables = entry.anatomy?.lists?.tables\n const adds = tables === undefined || tables.length > 0 || (entry.anatomy?.counts?.migrations ?? 0) > 0\n const migrate = moduleInstall && adds && !steps(command).includes('yarn db:migrate')\n return [command, ...(generate ? ['yarn generate'] : []), ...(migrate ? ['yarn db:migrate'] : [])].join(' && ')\n}\n\nfunction installText(\n install: Pick<ModuleCard, 'install' | 'presetNote' | 'installEmptyPreset'>,\n entry: CatalogEntry,\n): string {\n if (install.presetNote === undefined) return withFollowUpSteps(install.install, entry)\n const empty = install.installEmptyPreset ?? null\n return newAppInstallText(entry.moduleIds, empty === null ? null : withFollowUpSteps(empty, entry))\n}\n\nfunction entryHeading(entry: CatalogEntry, line: string | null): string {\n const name = entry.npm ?? entry.locator.replace(/^github:/, '')\n const version = installVersion(entry, line)\n const origin = ORIGIN_LABEL[entry.type] ?? entry.type\n return `${name}${version ? ` ${version}` : ''} ${levelLabel(entry, line)} ${origin} \u00B7 ${licenseLabel(entry)}`\n}\n\n/** `today` (YYYY-MM-DD) decides whether `installable from` is still ahead (Yarn age gate). */\nexport function renderSearchHuman(view: ModuleSearchView, today: string = new Date().toISOString().slice(0, 10)): string[] {\n const { json } = view\n const out: string[] = []\n if (view.lineNote) out.push(view.lineNote, '')\n if (json.didYouMean) out.push(`Nothing matched \"${json.query}\". Showing results for \"${json.didYouMean}\".`, '')\n if (json.builtIn.length > 0) {\n out.push('Already in Open Mercato core:')\n const idWidth = Math.max(...json.builtIn.map((card) => card.id.length))\n const titleWidth = Math.max(...json.builtIn.map((card) => card.title.length))\n for (const card of json.builtIn) {\n const action = card.enabled ? 'Already enabled.' : card.enabled === false ? card.install : (card.presetNote ?? card.install)\n out.push(` ${card.id.padEnd(idWidth)} ${card.title.padEnd(titleWidth)} ${action}`)\n }\n out.push('')\n }\n for (const card of json.results) {\n const entry = view.entries.get(card.locator)\n if (!entry) continue\n out.push(entryHeading(entry, json.line))\n if (card.summary) out.push(` ${card.summary}`)\n const details = [\n card.matchedOn.length > 0 ? `matched: ${card.matchedOn.join(', ')}` : null,\n policyLabel(card.agentPolicy) ? `agent policy: ${policyLabel(card.agentPolicy)}` : null,\n ].filter((item): item is string => item !== null)\n if (details.length > 0) out.push(` ${details.join(' \u00B7 ')}`)\n if (card.installableFrom && card.installableFrom > today && card.status !== 'verified') {\n out.push(` installable from: ${card.installableFrom}`)\n }\n out.push(` install: ${installText(card, entry)}`)\n out.push(` more: ${card.card}`)\n out.push('')\n }\n const hiddenLine =\n json.hidden > 0 && json.line !== null\n ? json.hidden === 1\n ? `1 more module is listed but not verified on ${json.line}. Use --all-lines.`\n : `${json.hidden} more modules are listed but not verified on ${json.line}. Use --all-lines.`\n : null\n if (json.results.length === 0 && json.builtIn.length === 0) {\n const what = json.query ? `\"${json.query}\"` : 'the filters'\n out.push(hiddenLine ? `No modules verified on ${json.line} matched ${what}.` : `No modules matched ${what}.`)\n if (hiddenLine) out.push(hiddenLine)\n out.push(`Browse the catalog: ${MODULES_SITE_URL}`)\n } else if (hiddenLine) {\n out.push(hiddenLine)\n }\n while (out.length > 0 && out[out.length - 1] === '') out.pop()\n return out\n}\n\nexport type ModuleInfoJson =\n | {\n schemaVersion: 1\n line: string | null\n entry: CatalogEntry | null\n candidates: string[]\n anatomy: null\n warnings: string[]\n untrusted: null\n card: string | null\n }\n | { schemaVersion: 1; error: string; candidates: string[] }\n\nexport type ModuleInfoView = {\n json: ModuleInfoJson\n builtIn: BuiltInModule | null\n builtInEnabled: boolean | null\n found: boolean\n}\n\nexport function buildInfoView(searcher: CatalogSearcher, ref: string, app: AppContext, today: string): ModuleInfoView {\n const builtInModules = searcher.builtIn?.modules ?? []\n const entries = searcher.index.entries\n const latest = latestLine(searcher.index.lines)?.key ?? null\n const line = app.omVersion ? resolveLine(app.omVersion, searcher.index.lines).line : latest\n const resolved = resolveRef(ref, entries, builtInModules)\n if (resolved.kind === 'builtin') {\n const coreModule = resolved.builtIn\n return {\n json: {\n schemaVersion: 1,\n line,\n entry: null,\n candidates: resolved.candidates,\n anatomy: null,\n warnings: builtInWarnings(coreModule, line),\n untrusted: null,\n card: cardUrl(searcher, coreModule.page),\n },\n builtIn: coreModule,\n builtInEnabled: builtInEnabled(coreModule.id, app.enabledModules),\n found: true,\n }\n }\n if (resolved.kind === 'none') {\n const id = stripVersion(ref).name.trim().toLowerCase()\n const shared = entries.filter((entry) => entry.moduleIds.includes(id))\n if (shared.length > 1) {\n return {\n json: {\n schemaVersion: 1,\n line,\n entry: null,\n candidates: resolved.candidates,\n anatomy: null,\n warnings: [sharedModuleIdWarning(id, shared.length)],\n untrusted: null,\n card: null,\n },\n builtIn: null,\n builtInEnabled: null,\n found: true,\n }\n }\n return {\n json: { schemaVersion: 1, error: unknownModuleMessage(ref, resolved.candidates), candidates: resolved.candidates },\n builtIn: null,\n builtInEnabled: null,\n found: false,\n }\n }\n const entry = resolved.entry\n return {\n json: {\n schemaVersion: 1,\n line,\n entry,\n candidates: [],\n anatomy: null,\n warnings: entryWarnings(entry, today, line, receiptSourceOf(searcher)),\n untrusted: null,\n card: cardUrl(searcher, entry.links.md),\n },\n builtIn: null,\n builtInEnabled: null,\n found: true,\n }\n}\n\nfunction plural(count: number, one: string, many: string): string {\n return `${count} ${count === 1 ? one : many}`\n}\n\nfunction anatomyLine(entry: CatalogEntry): string | null {\n const lists = entry.anatomy?.lists\n if (!lists) return null\n const parts = [\n lists.tables && lists.tables.length > 0 ? plural(lists.tables.length, 'table', 'tables') : null,\n lists.aclFeatures && lists.aclFeatures.length > 0 ? plural(lists.aclFeatures.length, 'permission', 'permissions') : null,\n lists.eventsEmitted && lists.eventsEmitted.length > 0 ? `emits ${plural(lists.eventsEmitted.length, 'event', 'events')}` : null,\n lists.publicRoutes && lists.publicRoutes.length > 0\n ? plural(lists.publicRoutes.length, 'public route', 'public routes')\n : null,\n ].filter((item): item is string => item !== null)\n return parts.length > 0 ? parts.join(' \u00B7 ') : null\n}\n\nexport function renderInfoHuman(view: ModuleInfoView, searcher: CatalogSearcher): string[] {\n const { json } = view\n if ('error' in json) return [json.error]\n const out: string[] = []\n if (view.builtIn) {\n const coreModule = view.builtIn\n out.push(`${coreModule.id} is built into Open Mercato core: ${coreModule.title}.`)\n if (coreModule.description) out.push(` ${coreModule.description}`)\n const presetNote = view.builtInEnabled === false ? null : builtInPresetText(coreModule, json.line)\n if (view.builtInEnabled) out.push(' Already enabled.')\n else if (presetNote) out.push(` ${presetNote}`)\n else out.push(` enable: ${coreModule.install || builtInEnableCommand(coreModule.id)}`)\n if (json.candidates.length > 0) out.push(` catalog modules with the same id: ${json.candidates.join(', ')}`)\n if (json.card) out.push(` more: ${json.card}`)\n return out\n }\n const entry = json.entry\n if (!entry) {\n out.push(...json.warnings)\n if (json.candidates.length > 0) out.push(`Candidates: ${json.candidates.join(', ')}`)\n return out\n }\n const line = json.line\n out.push(entryHeading(entry, line))\n out.push(` ${entry.title}: ${entry.summary}`)\n if (entry.whenToUse) out.push(` ${entry.whenToUse}`)\n const lines = searcher.index.lines.map((item) => `${item.key} ${statusOn(entry, item.key)}`)\n if (lines.length > 0) out.push(` lines: ${lines.join(' \u00B7 ')}`)\n const policy = line ? (entry.agentPolicy[line] ?? null) : null\n if (policy && line) {\n out.push(` agent policy on ${line}: ${policy.decision}`)\n for (const reason of policy.reasons.slice(0, 5)) out.push(` ${reason.code}: ${reason.text}`)\n }\n const adds = anatomyLine(entry)\n if (adds) out.push(` adds: ${adds}`)\n const lists = entry.anatomy?.lists\n const settings = [...(lists?.env ?? []), ...(lists?.credentials ?? []).map((credential) => credential.key)]\n if (settings.length > 0) out.push(` needs settings: ${settings.slice(0, 10).join(', ')}`)\n if (lists?.hosts && lists.hosts.length > 0) out.push(` talks to: ${lists.hosts.slice(0, 10).join(', ')}`)\n const install = installFields(entry, line, latestLine(searcher.index.lines)?.key ?? null, peerLookup(searcher))\n out.push(` install: ${installText(install, entry)}`)\n for (const warning of json.warnings) out.push(` warning: ${warning}`)\n out.push(` install plan: ${json.card ?? entry.links.md}`)\n out.push(` more: ${cardUrl(searcher, entry.links.page)}`)\n return out\n}\n\nexport type ModuleSearchDeps = {\n appRoot?: string\n env?: NodeJS.ProcessEnv\n fetchImpl?: FetchLike\n now?: () => number\n stdout?: (line: string) => void\n stderr?: (line: string) => void\n readEnabledModules?: ReadEnabledModules | null\n}\n\nconst SEARCH_USAGE =\n 'Usage: yarn mercato module search [<query>] [--category <id>] [--kind <kind>] [--all-lines] [--limit <n>] [--json] (a query, a --category or a --kind)'\nconst INFO_USAGE = 'Usage: yarn mercato module info <package-or-module-id> [--json]'\n\nexport const BUILT_IN_UNAVAILABLE_MESSAGE =\n 'Built-in module list unavailable; results may miss modules already in Open Mercato core.'\n\ntype LoadedForCommand = { searcher: CatalogSearcher; app: AppContext; builtInUnavailable: boolean }\n\nasync function loadForCommand(deps: ModuleSearchDeps): Promise<LoadedForCommand> {\n const env = deps.env ?? process.env\n const appRoot = deps.appRoot ?? createResolver().getAppDir()\n const stderr = deps.stderr ?? ((line: string) => console.error(line))\n const url = env.OM_MODULES_INDEX_URL?.trim() || DEFAULT_MODULES_INDEX_URL\n const builtInUrl = env.OM_MODULES_BUILTIN_URL?.trim() || undefined\n const loaded = await loadIndex({\n appRoot,\n url,\n ...(builtInUrl ? { builtInUrl } : {}),\n ...(deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}),\n ...(deps.now ? { now: deps.now } : {}),\n })\n if (loaded.source === 'stale-cache') stderr(`Using cached module index from ${loaded.fetchedAt}.`)\n if (loaded.builtInUnavailable !== null) {\n stderr(BUILT_IN_UNAVAILABLE_MESSAGE)\n stderr(`Reason: ${loaded.builtInUnavailable}`)\n }\n const app = detectAppContext(appRoot, deps.readEnabledModules ?? null, env)\n return {\n searcher: loaded.searcher,\n app,\n builtInUnavailable: loaded.builtInUnavailable !== null,\n }\n}\n\nfunction handleLoadError(error: unknown, stderr: (line: string) => void): number {\n if (error instanceof ModuleIndexUnavailableError) {\n stderr(MODULE_INDEX_UNAVAILABLE_MESSAGE)\n stderr(`Reason: ${error.detail}`)\n return MODULE_INDEX_UNAVAILABLE_EXIT_CODE\n }\n throw error\n}\n\nexport async function runModuleSearch(args: string[], deps: ModuleSearchDeps = {}): Promise<number> {\n const stdout = deps.stdout ?? ((line: string) => console.log(line))\n const stderr = deps.stderr ?? ((line: string) => console.error(line))\n const parsed = parseModuleSearchArgs(args)\n if (!parsed.query && !parsed.category && !parsed.kinds) {\n stderr(SEARCH_USAGE)\n return 1\n }\n let loaded: LoadedForCommand\n try {\n loaded = await loadForCommand(deps)\n } catch (error: unknown) {\n return handleLoadError(error, stderr)\n }\n const view = buildSearchView(\n loaded.searcher,\n { query: parsed.query, category: parsed.category, kinds: parsed.kinds, allLines: parsed.allLines, limit: parsed.limit },\n loaded.app,\n )\n if (loaded.builtInUnavailable) view.json.builtInUnavailable = true\n const today = new Date(deps.now ? deps.now() : Date.now()).toISOString().slice(0, 10)\n if (parsed.json) stdout(JSON.stringify(view.json, null, 2))\n else for (const line of renderSearchHuman(view, today)) stdout(line)\n return 0\n}\n\nexport async function runModuleInfo(args: string[], deps: ModuleSearchDeps = {}): Promise<number> {\n const stdout = deps.stdout ?? ((line: string) => console.log(line))\n const stderr = deps.stderr ?? ((line: string) => console.error(line))\n const parsed = parseModuleInfoArgs(args)\n if (!parsed.ref) {\n stderr(INFO_USAGE)\n return 1\n }\n let loaded: LoadedForCommand\n try {\n loaded = await loadForCommand(deps)\n } catch (error: unknown) {\n return handleLoadError(error, stderr)\n }\n const today = new Date(deps.now ? deps.now() : Date.now()).toISOString().slice(0, 10)\n const view = buildInfoView(loaded.searcher, parsed.ref, loaded.app, today)\n if (parsed.json) stdout(JSON.stringify(view.json, null, 2))\n else if (view.found) for (const line of renderInfoHuman(view, loaded.searcher)) stdout(line)\n else for (const line of renderInfoHuman(view, loaded.searcher)) stderr(line)\n return view.found ? 0 : 1\n}\n"],
5
+ "mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AAEjB,SAAS,sBAAsB;AAC/B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAUK;AACP,SAAS,qBAAqB,6BAA6B;AAEpD,MAAM,4BAA4B;AAClC,MAAM,mBAAmB;AACzB,MAAM,yBAAyB,KAAK,OAAO;AAC3C,MAAM,0BAA0B;AAChC,MAAM,sBAAsB,KAAK,KAAK;AACtC,MAAM,0BAA0B,KAAK,KAAK,YAAY,SAAS,oBAAoB;AACnF,MAAM,qCAAqC;AAC3C,MAAM,mCACX;AA8CK,MAAM,oCAAoC,MAAM;AAAA,EAGrD,YAAY,QAAgB;AAC1B,UAAM,GAAG,gCAAgC,KAAK,MAAM,GAAG;AACvD,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,EAChB;AACF;AAEA,MAAM,WAAW,CAAC,UAChB,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AACrE,MAAM,WAAW,CAAC,UAAoC,OAAO,UAAU;AACvE,MAAM,gBAAgB,CAAC,UAAsC,MAAM,QAAQ,KAAK,KAAK,MAAM,MAAM,QAAQ;AACzG,MAAM,mBAAmB,CAAC,UAA2C,UAAU,QAAQ,SAAS,KAAK;AACrG,MAAM,YAAY,CAAC,OAAgB,SAA+C,MAAM,QAAQ,KAAK,KAAK,MAAM,MAAM,IAAI;AAC1H,MAAM,aAAa,CAAC,OAAgB,SAClC,SAAS,KAAK,KAAK,OAAO,OAAO,KAAK,EAAE,MAAM,IAAI;AACpD,MAAM,aAAa,CAAC,OAAgB,UAAgD,UAAU,UAAa,MAAM,KAAK;AAEtH,MAAM,gBAAgB,CAAC,eAAe,iBAAiB,kBAAkB,OAAO,SAAS,UAAU,cAAc;AAGjH,SAAS,UAAU,OAAyB;AAC1C,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,MAAI,CAAC,SAAS,KAAK,EAAG,QAAO;AAC7B,QAAM,QAAQ,MAAM;AACpB,MAAI,UAAU,OAAW,QAAO;AAChC,SACE,SAAS,KAAK,KACd,cAAc,MAAM,CAAC,QAAQ,WAAW,MAAM,GAAG,GAAG,aAAa,CAAC,KAClE;AAAA,IAAW,MAAM;AAAA,IAAa,CAAC,gBAC7B,UAAU,aAAa,CAAC,eAAe,SAAS,UAAU,KAAK,SAAS,WAAW,GAAG,CAAC;AAAA,EACzF;AAEJ;AAEA,SAAS,cAAc,OAAyB;AAC9C,SACE,SAAS,KAAK,KACd,SAAS,MAAM,QAAQ,KACvB,UAAU,MAAM,SAAS,CAAC,WAAW,SAAS,MAAM,KAAK,SAAS,OAAO,IAAI,KAAK,SAAS,OAAO,IAAI,CAAC;AAE3G;AAEA,SAAS,WAAW,OAAyB;AAC3C,SACE,SAAS,KAAK,KACd,SAAS,MAAM,IAAI,KACnB,iBAAiB,MAAM,KAAK,KAC5B,cAAc,MAAM,KAAK,KACzB,WAAW,MAAM,QAAQ,CAAC,WAAW,WAAW,QAAQ,QAAQ,CAAC;AAErE;AAEA,SAAS,SAAS,OAAyB;AACzC,SACE,SAAS,KAAK,KACd;AAAA,IACE,MAAM;AAAA,IACN,CAAC,SACC,SAAS,IAAI,KACb,SAAS,KAAK,MAAM,KACpB,WAAW,KAAK,IAAI,gBAAgB,KACpC,WAAW,KAAK,eAAe,gBAAgB;AAAA,EACnD,KACA,cAAc,MAAM,UAAU,KAC9B,WAAW,MAAM,UAAU,UAAU,KACrC,WAAW,MAAM,WAAW,aAAa,KACzC,WAAW,MAAM,eAAe,aAAa,KAC7C,WAAW,MAAM,OAAO,CAAC,UAAU,WAAW,OAAO,QAAQ,CAAC,KAC9D;AAAA,IAAW,MAAM;AAAA,IAAW,CAAC,cAC3B,WAAW,WAAW,CAAC,SAAS,SAAS,IAAI,KAAK,SAAS,KAAK,MAAM,KAAK,iBAAiB,KAAK,aAAa,CAAC;AAAA,EACjH;AAEJ;AAEA,SAAS,UAAU,OAAyB;AAC1C,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,SACE,SAAS,KAAK,KACd,WAAW,MAAM,aAAa,CAAC,SAAS,SAAS,IAAI,KAAK,SAAS,KAAK,MAAM,KAAK,cAAc,KAAK,OAAO,CAAC,KAC9G,WAAW,MAAM,SAAS,CAAC,SAAS,SAAS,IAAI,KAAK,SAAS,KAAK,MAAM,KAAK,cAAc,KAAK,OAAO,CAAC,KAC1G,WAAW,MAAM,SAAS,QAAQ;AAEtC;AAIA,SAAS,eAAe,OAAuC;AAC7D,MAAI,CAAC,SAAS,KAAK,EAAG,QAAO;AAC7B,QAAM,UAAU,MAAM;AACtB,QAAM,UAAU,MAAM;AACtB,QAAM,QAAQ,MAAM;AACpB,QAAM,UAAU,MAAM;AACtB,SACE,SAAS,MAAM,OAAO,KACtB,SAAS,MAAM,IAAI,MAClB,MAAM,QAAQ,QAAQ,SAAS,MAAM,GAAG,MACzC,SAAS,MAAM,KAAK,KACpB,SAAS,MAAM,OAAO,KACtB,SAAS,MAAM,SAAS,KACxB,SAAS,MAAM,IAAI,KACnB,SAAS,MAAM,IAAI,KACnB,SAAS,MAAM,QAAQ,KACvB,cAAc,MAAM,IAAI,KACxB,UAAU,MAAM,WAAW,CAAC,aAAa,SAAS,QAAQ,KAAK,SAAS,SAAS,IAAI,KAAK,SAAS,SAAS,KAAK,CAAC,KAClH,cAAc,MAAM,SAAS,KAC7B;AAAA,IAAW,MAAM;AAAA,IAAU,CAAC,aAC1B;AAAA,MACE;AAAA,MACA,CAAC,gBAAgB,SAAS,WAAW,KAAK,SAAS,YAAY,GAAG,KAAK,WAAW,YAAY,YAAY,gBAAgB;AAAA,IAC5H;AAAA,EACF,KACA,cAAc,MAAM,WAAW,KAC/B,SAAS,MAAM,MAAM,KACrB,OAAO,MAAM,WAAW,aACxB,OAAO,MAAM,aAAa,aAC1B,iBAAiB,MAAM,IAAI,KAC3B,SAAS,OAAO,KAChB,iBAAiB,QAAQ,IAAI,KAC7B,SAAS,QAAQ,IAAI,KACrB,SAAS,OAAO,KAChB,iBAAiB,QAAQ,MAAM,KAC/B,iBAAiB,QAAQ,OAAO,KAChC,iBAAiB,QAAQ,eAAe,KACxC,iBAAiB,QAAQ,MAAM,KAC/B,WAAW,QAAQ,aAAa,gBAAgB,KAChD,SAAS,MAAM,MAAM,KACrB,UAAU,MAAM,OAAO,KACvB,UAAU,MAAM,OAAO,KACvB,WAAW,MAAM,aAAa,aAAa,KAC3C,SAAS,OAAO,KAChB,SAAS,QAAQ,GAAG,KACpB,WAAW,QAAQ,YAAY,gBAAgB,KAC/C,SAAS,KAAK,KACd,SAAS,MAAM,IAAI,KACnB,SAAS,MAAM,EAAE;AAErB;AAEO,SAAS,kBAAkB,OAAqC;AACrE,MAAI,CAAC,SAAS,KAAK,KAAK,MAAM,kBAAkB,EAAG,QAAO;AAC1D,MAAI,CAAC,SAAS,MAAM,WAAW,KAAK,CAAC,SAAS,MAAM,WAAW,KAAK,CAAC,SAAS,MAAM,IAAI,EAAG,QAAO;AAClG,MAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,KAAK,CAAC,MAAM,QAAQ,MAAM,OAAO,EAAG,QAAO;AACzE,MAAI,CAAC,WAAW,MAAM,UAAU,QAAQ,EAAG,QAAO;AAClD,MAAI,CAAC,MAAM,MAAM,MAAM,CAAC,SAAS,SAAS,IAAI,KAAK,SAAS,KAAK,GAAG,KAAK,SAAS,KAAK,OAAO,KAAK,SAAS,KAAK,MAAM,CAAC,GAAG;AACzH,WAAO;AAAA,EACT;AACA,MAAI,CAAC,MAAM,QAAQ,MAAM,cAAc,EAAG,QAAO;AACjD,QAAM,QAAQ,MAAM,MAAM;AAAA,IACxB,CAAC,SACC,KAAK,WAAW,YAAY,KAAK,WAAW,cAAc,KAAK,WAAW;AAAA,EAC9E;AACA,SAAO,EAAE,GAAI,OAAwB,MAAM;AAC7C;AAEO,SAAS,kBAAkB,OAAqC;AACrE,MAAI,CAAC,SAAS,KAAK,KAAK,MAAM,kBAAkB,KAAK,CAAC,SAAS,MAAM,WAAW,KAAK,CAAC,MAAM,QAAQ,MAAM,OAAO,GAAG;AAClH,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,MAAM,QAAQ;AAAA,IAC1B,CAAC,eACC,SAAS,UAAU,KACnB,SAAS,WAAW,EAAE,KACtB,SAAS,WAAW,KAAK,KACzB,SAAS,WAAW,WAAW,KAC/B,SAAS,WAAW,QAAQ,KAC5B,SAAS,WAAW,OAAO,KAC3B,SAAS,WAAW,IAAI,KACxB,WAAW,WAAW,UAAU,aAAa,KAC7C,WAAW,WAAW,WAAW,aAAa,KAC9C,WAAW,WAAW,eAAe,aAAa,KAClD,UAAU,WAAW,OAAO;AAAA,EAChC;AACA,SAAO,QAAS,QAAyB;AAC3C;AAEA,SAAS,eAAe,OAAkC;AACxD,MAAI,CAAC,SAAS,KAAK,KAAK,MAAM,iBAAiB,EAAG,QAAO;AACzD,MAAI,CAAC,SAAS,MAAM,GAAG,KAAK,CAAC,SAAS,MAAM,UAAU,KAAK,CAAC,SAAS,MAAM,SAAS,EAAG,QAAO;AAC9F,QAAM,QAAQ,kBAAkB,MAAM,KAAK;AAC3C,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,UAAU,MAAM,YAAY,OAAO,OAAO,kBAAkB,MAAM,OAAO;AAC/E,SAAO;AAAA,IACL,cAAc;AAAA,IACd,KAAK,MAAM;AAAA,IACX,YAAY,MAAM;AAAA,IAClB,WAAW,MAAM;AAAA,IACjB,MAAM,SAAS,MAAM,IAAI,IAAI,MAAM,OAAO;AAAA,IAC1C,aAAa,SAAS,MAAM,WAAW,IAAI,MAAM,cAAc;AAAA,IAC/D;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,kBAAkB,UAA0B;AAC1D,QAAM,MAAM,IAAI,IAAI,QAAQ;AAC5B,MAAI,SAAS;AACb,MAAI,OAAO;AACX,MAAI,WAAW,IAAI,SAAS,QAAQ,UAAU,eAAe;AAC7D,SAAO,IAAI,SAAS;AACtB;AAEA,SAAS,UAAU,MAAgC;AACjD,MAAI;AACF,WAAO,eAAe,KAAK,MAAM,GAAG,aAAa,MAAM,MAAM,CAAC,CAAC;AAAA,EACjE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,WAAW,MAAc,OAAwB;AACxD,MAAI;AACF,OAAG,UAAU,KAAK,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AACpD,UAAM,YAAY,GAAG,IAAI,IAAI,QAAQ,GAAG;AACxC,OAAG,cAAc,WAAW,KAAK,UAAU,KAAK,CAAC;AACjD,OAAG,WAAW,WAAW,IAAI;AAAA,EAC/B,QAAQ;AACN;AAAA,EACF;AACF;AAEA,SAAS,iBAAiB,OAAqB;AAC7C,QAAM,MAAM,IAAI,IAAI,KAAK;AACzB,QAAM,QAAQ,IAAI,aAAa,eAAe,IAAI,aAAa,eAAe,IAAI,aAAa;AAC/F,MAAI,IAAI,aAAa,YAAY,EAAE,IAAI,aAAa,WAAW,QAAQ;AACrE,UAAM,IAAI,MAAM,qEAAqE;AAAA,EACvF;AACF;AAEA,eAAe,UACb,WACA,KACA,MACA,WAC4B;AAC5B,QAAM,UAAkC,EAAE,QAAQ,mBAAmB;AACrE,MAAI,KAAM,SAAQ,eAAe,IAAI;AACrC,QAAM,SAAS,YAAY,QAAQ,SAAS;AAC5C,MAAI,SAAS;AACb,WAAS,YAAY,GAAG,aAAa,GAAG,aAAa,GAAG;AACtD,qBAAiB,MAAM;AACvB,UAAM,WAAW,MAAM,UAAU,QAAQ,EAAE,SAAS,QAAQ,UAAU,SAAS,CAAC;AAChF,QAAI,CAAC,CAAC,KAAK,KAAK,KAAK,KAAK,GAAG,EAAE,SAAS,SAAS,MAAM,EAAG,QAAO;AACjE,UAAM,SAAS,MAAM,OAAO;AAC5B,UAAM,WAAW,SAAS,QAAQ,IAAI,UAAU;AAChD,QAAI,CAAC,YAAY,cAAc,EAAG,OAAM,IAAI,MAAM,wDAAwD;AAC1G,aAAS,IAAI,IAAI,UAAU,MAAM,EAAE,SAAS;AAAA,EAC9C;AACA,QAAM,IAAI,MAAM,6CAA6C;AAC/D;AAEA,eAAe,gBAAgB,UAA+C;AAC5E,QAAM,WAAW,MAAM,IAAI,MAAM,mCAAmC,sBAAsB,QAAQ;AAClG,QAAM,SAAS,OAAO,SAAS,QAAQ,IAAI,gBAAgB,CAAC;AAC5D,MAAI,SAAS,wBAAwB;AACnC,UAAM,SAAS,MAAM,OAAO;AAC5B,UAAM,SAAS;AAAA,EACjB;AACA,MAAI,CAAC,SAAS,MAAM;AAClB,UAAM,QAAQ,MAAM,SAAS,KAAK;AAClC,QAAI,OAAO,WAAW,KAAK,UAAU,KAAK,KAAK,EAAE,IAAI,uBAAwB,OAAM,SAAS;AAC5F,WAAO;AAAA,EACT;AACA,QAAM,SAAS,SAAS,KAAK,UAAU;AACvC,QAAM,SAAuB,CAAC;AAC9B,MAAI,QAAQ;AACZ,MAAI;AACF,WAAO,MAAM;AACX,YAAM,QAAQ,MAAM,OAAO,KAAK;AAChC,UAAI,MAAM,KAAM;AAChB,eAAS,MAAM,MAAM;AACrB,UAAI,QAAQ,wBAAwB;AAClC,cAAM,OAAO,OAAO;AACpB,cAAM,SAAS;AAAA,MACjB;AACA,aAAO,KAAK,MAAM,KAAK;AAAA,IACzB;AACA,WAAO,KAAK,MAAM,OAAO,OAAO,MAAM,EAAE,SAAS,MAAM,CAAC;AAAA,EAC1D,UAAE;AACA,WAAO,YAAY;AAAA,EACrB;AACF;AAEA,eAAe,eACb,WACA,KACA,MACA,WAC4B;AAC5B,MAAI,YAAqB;AACzB,WAAS,UAAU,GAAG,UAAU,GAAG,WAAW,GAAG;AAC/C,QAAI;AACF,YAAM,WAAW,MAAM,UAAU,WAAW,KAAK,MAAM,SAAS;AAChE,UAAI,SAAS,UAAU,KAAK;AAC1B,cAAM,SAAS,MAAM,OAAO;AAC5B,oBAAY,IAAI,MAAM,GAAG,GAAG,aAAa,SAAS,MAAM,EAAE;AAC1D;AAAA,MACF;AACA,aAAO;AAAA,IACT,SAAS,OAAgB;AACvB,kBAAY;AAAA,IACd;AAAA,EACF;AACA,QAAM,qBAAqB,QAAQ,YAAY,IAAI,MAAM,OAAO,SAAS,CAAC;AAC5E;AAEA,eAAe,UACb,WACA,KACA,MACA,WACA,OACwF;AACxF,QAAM,WAAW,MAAM,eAAe,WAAW,KAAK,MAAM,SAAS;AACrE,MAAI,SAAS,WAAW,IAAK,QAAO,EAAE,aAAa,KAAK;AACxD,MAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,GAAG,GAAG,aAAa,SAAS,MAAM,EAAE;AACtE,QAAM,QAAQ,MAAM,MAAM,gBAAgB,QAAQ,CAAC;AACnD,MAAI,UAAU,KAAM,OAAM,IAAI,MAAM,GAAG,GAAG,6CAA6C;AACvF,SAAO,EAAE,aAAa,OAAO,OAAO,MAAM,SAAS,QAAQ,IAAI,MAAM,EAAE;AACzE;AAEA,SAAS,WAAW,OAAqB,SAAsD;AAC7F,MAAI;AACF,WAAO,sBAAsB,OAAO,OAAO;AAAA,EAC7C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,UAAU,SAAuD;AACrF,QAAM,MAAM,QAAQ,OAAO;AAC3B,MAAI;AACJ,MAAI;AACF,qBAAiB,GAAG;AACpB,iBAAa,QAAQ,cAAc,kBAAkB,GAAG;AACxD,qBAAiB,UAAU;AAAA,EAC7B,SAAS,OAAgB;AACvB,UAAM,IAAI,4BAA4B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,EAC9F;AACA,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,YAAuB,QAAQ,cAAc,CAAC,OAAO,SAAS,MAAM,OAAO,IAAI;AACrF,QAAM,MAAM,QAAQ,QAAQ,MAAM,KAAK,IAAI;AAC3C,QAAM,YAAY,KAAK,KAAK,QAAQ,SAAS,uBAAuB;AACpE,QAAM,SAAS,UAAU,SAAS;AAClC,MAAI,SAAS,UAAU,OAAO,QAAQ,OAAO,OAAO,eAAe,aAAa,SAAS;AAGzF,MAAI,UAAU,OAAO,WAAW,IAAI,IAAI,KAAK,MAAM,OAAO,SAAS,IAAI,OAAO;AAC5E,UAAM,WAAW,WAAW,OAAO,OAAO,OAAO,OAAO;AACxD,QAAI,UAAU;AACZ,aAAO,EAAE,OAAO,OAAO,OAAO,SAAS,OAAO,SAAS,oBAAoB,MAAM,QAAQ,SAAS,WAAW,OAAO,WAAW,SAAS;AAAA,IAC1I;AAEA,aAAS;AAAA,EACX;AAEA,MAAI;AACF,UAAM,cAAc,MAAM,UAAU,WAAW,KAAK,QAAQ,QAAQ,MAAM,WAAW,iBAAiB;AACtG,QAAI;AACJ,QAAI;AACJ,QAAI,YAAY,aAAa;AAC3B,UAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,GAAG,GAAG,qCAAqC;AACxE,cAAQ,OAAO;AACf,aAAO,OAAO;AAAA,IAChB,OAAO;AACL,cAAQ,YAAY;AACpB,aAAO,YAAY;AAAA,IACrB;AACA,QAAI,UAAU,QAAQ,WAAW;AACjC,QAAI,cAAc,QAAQ,eAAe;AACzC,QAAI,qBAAoC;AACxC,QAAI;AACF,YAAM,gBAAgB,MAAM;AAAA,QAC1B;AAAA,QACA;AAAA,QACA,QAAQ,UAAU,cAAc;AAAA,QAChC;AAAA,QACA;AAAA,MACF;AACA,UAAI,CAAC,cAAc,aAAa;AAC9B,kBAAU,cAAc;AACxB,sBAAc,cAAc;AAAA,MAC9B;AAAA,IACF,SAAS,OAAgB;AACvB,gBAAU,QAAQ,WAAW;AAC7B,UAAI,CAAC,QAAS,sBAAqB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,IAC1F;AAEA,UAAM,WAAW,sBAAsB,OAAO,OAAO;AACrD,UAAM,YAAY,IAAI,KAAK,IAAI,CAAC,EAAE,YAAY;AAC9C,eAAW,WAAW;AAAA,MACpB,cAAc;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AACD,WAAO,EAAE,OAAO,SAAS,oBAAoB,QAAQ,WAAW,WAAW,SAAS;AAAA,EACtF,SAAS,OAAgB;AACvB,UAAM,WAAW,SAAS,WAAW,OAAO,OAAO,OAAO,OAAO,IAAI;AACrE,QAAI,UAAU,UAAU;AACtB,aAAO;AAAA,QACL,OAAO,OAAO;AAAA,QACd,SAAS,OAAO;AAAA,QAChB,oBAAoB,OAAO,UAAU,OAAO;AAAA,QAC5C,QAAQ;AAAA,QACR,WAAW,OAAO;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AACA,UAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACpE,UAAM,IAAI,4BAA4B,MAAM;AAAA,EAC9C;AACF;AAOA,SAAS,aAAa,MAAuB;AAC3C,MAAI;AACF,WAAO,KAAK,MAAM,GAAG,aAAa,MAAM,MAAM,CAAC;AAAA,EACjD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,cAAc,SAAgC;AAC5D,QAAM,YAAY,aAAa,KAAK,KAAK,SAAS,gBAAgB,iBAAiB,QAAQ,cAAc,CAAC;AAC1G,MAAI,SAAS,SAAS,KAAK,SAAS,UAAU,OAAO,EAAG,QAAO,UAAU;AACzE,QAAM,WAAW,aAAa,KAAK,KAAK,SAAS,cAAc,CAAC;AAChE,MAAI,CAAC,SAAS,QAAQ,EAAG,QAAO;AAChC,aAAW,SAAS,CAAC,gBAAgB,iBAAiB,GAAY;AAChE,UAAM,OAAO,SAAS,KAAK;AAC3B,QAAI,SAAS,IAAI,KAAK,SAAS,KAAK,YAAY,CAAC,GAAG;AAClD,YAAM,QAAQ,KAAK,YAAY,EAAE,KAAK;AACtC,YAAM,QAAQ,yCAAyC,KAAK,KAAK;AACjE,aAAO,QAAQ,MAAM,CAAC,IAAI;AAAA,IAC5B;AAAA,EACF;AACA,SAAO;AACT;AAIO,SAAS,iBACd,SACA,aACA,MAAyB,QAAQ,KACrB;AACZ,QAAM,cAAc,KAAK,KAAK,SAAS,OAAO,YAAY;AAC1D,MAAI,iBAAuC;AAC3C,MAAI,eAAe,GAAG,WAAW,WAAW,GAAG;AAC7C,QAAI;AACF,uBAAiB,YAAY,aAAa,GAAG;AAAA,IAC/C,QAAQ;AACN,uBAAiB;AAAA,IACnB;AAAA,EACF;AACA,SAAO,EAAE,WAAW,cAAc,OAAO,GAAG,eAAe;AAC7D;AAmCA,SAAS,eAAe,IAAY,SAA+C;AACjF,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,QAAQ,KAAK,CAAC,eAAe,WAAW,OAAO,OAAO,WAAW,QAAQ,kBAAkB,YAAY;AAChH;AAEO,SAAS,gBACd,UACA,SACA,KACkB;AAClB,QAAM,WAAW,IAAI,YAAY,YAAY,IAAI,WAAW,SAAS,MAAM,KAAK,IAAI;AACpF,QAAM,YAAY;AAAA,IAChB,OAAO,QAAQ,SAAS;AAAA,IACxB,GAAI,IAAI,YAAY,EAAE,WAAW,IAAI,UAAU,IAAI,CAAC;AAAA,IACpD,GAAI,QAAQ,WAAW,EAAE,UAAU,QAAQ,SAAS,IAAI,CAAC;AAAA,IACzD,GAAI,QAAQ,QAAQ,EAAE,MAAM,QAAQ,MAAM,IAAI,CAAC;AAAA,EACjD;AACA,QAAM,aAAa,YAAY,UAAU,SAAS;AAClD,QAAM,OAAO,WAAW;AACxB,QAAM,WAAW,QAAQ,aAAa,QAAQ,SAAS;AACvD,QAAM,SAAS,WAAW,aAAa,YAAY,UAAU,EAAE,GAAG,WAAW,qBAAqB,KAAK,CAAC;AACxG,QAAM,OAAO,OAAO;AACpB,QAAM,SAAS,WAAW,IAAI,WAAW,QAAQ;AAAA,IAC/C,CAAC,QAAQ,IAAI,KAAK,MAAM,SAAS,aAAa,SAAS,QAAQ,CAAC,IAAI,KAAK,MAAM,QAAQ,YAAY,IAAI;AAAA,EACzG,EAAE;AACF,QAAM,QAAQ,KAAK,IAAI,kBAAkB,KAAK,IAAI,GAAG,KAAK,MAAM,QAAQ,SAAS,EAAE,CAAC,CAAC;AACrF,QAAM,UAAU,KAAK,MAAM,GAAG,KAAK,EAAE,IAAI,CAAC,QAAQ,aAAa,UAAU,KAAK,MAAM,OAAO,MAAM,CAAC;AAClG,QAAM,UAAU,oBAAI,IAA0B;AAC9C,aAAW,OAAO,KAAK,MAAM,GAAG,KAAK,EAAG,SAAQ,IAAI,IAAI,KAAK,MAAM,SAAS,IAAI,KAAK,KAAK;AAC1F,QAAM,UAAU,OAAO,QAAQ,IAAI,CAAC,UAAU,EAAE,GAAG,MAAM,SAAS,eAAe,KAAK,IAAI,IAAI,cAAc,EAAE,EAAE;AAChH,QAAM,WACJ,YAAY,SAAS,SAAS,OAC1B,gBAAgB,IAAI,SAAS,qCAAqC,SAAS,QAAQ,iBAAiB,uCACpG;AACN,SAAO;AAAA,IACL,MAAM;AAAA,MACJ,eAAe;AAAA,MACf,OAAO,QAAQ,SAAS;AAAA,MACxB;AAAA,MACA,WAAW,IAAI;AAAA,MACf;AAAA,MACA;AAAA,MACA,OAAO,WAAW,QAAQ;AAAA,MAC1B;AAAA,MACA,aAAa,SAAS,MAAM;AAAA,MAC5B,aAAa,SAAS,MAAM;AAAA,MAC5B,eAAe,gBAAgB,QAAQ;AAAA,MACvC,GAAI,OAAO,aAAa,EAAE,YAAY,OAAO,WAAW,IAAI,CAAC;AAAA,IAC/D;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,MAAM,eAAuC,EAAE,UAAU,YAAY,WAAW,aAAa,aAAa,YAAY;AAEtH,SAAS,WAAW,OAAqB,MAA6B;AACpE,QAAM,QAAQ,MAAM,UAAU,eAAe,MAAM,SAAS,aAAa,aAAa;AACtF,MAAI,SAAS,KAAM,QAAO;AAC1B,QAAM,SAAS,SAAS,OAAO,IAAI;AACnC,SAAO,WAAW,aAAa,GAAG,KAAK,SAAM,IAAI,KAAK,GAAG,KAAK,SAAM,MAAM,OAAO,IAAI;AACvF;AAEA,SAAS,YAAY,QAA2C;AAC9D,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,UAAU,OAAO,QAAQ,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,WAAW,OAAO,KAAK,YAAY,EAAE,QAAQ,MAAM,GAAG,CAAC;AACvG,SAAO,QAAQ,SAAS,IAAI,GAAG,OAAO,QAAQ,KAAK,QAAQ,KAAK,IAAI,CAAC,MAAM,OAAO;AACpF;AAEA,SAAS,aAAa,OAA6B;AACjD,SAAO,MAAM,QAAQ,QAAQ,MAAM,QAAQ;AAC7C;AAEA,MAAM,QAAQ,CAAC,YAAoB,QAAQ,MAAM,MAAM;AAQhD,SAAS,kBAAkB,SAAiB,OAA6B;AAC9E,QAAM,MAAM,QAAQ,WAAW,mCAAmC;AAClE,QAAM,YAAY,MAAM,SAAS,YAAY,MAAM,SAAS;AAC5D,QAAM,gBACH,OAAO,aAAc,QAAQ,SAAS,yBAAyB,KAAK,QAAQ,WAAW,sBAAsB;AAChH,QAAM,cAAc,QAAQ,WAAW,WAAW,MAAM,MAAM,SAAS,aAAa,MAAM,SAAS;AACnG,QAAM,YAAY,eAAe,QAAQ,CAAC,MAAM,OAAO,EAAE,SAAS,eAAe;AACjF,QAAM,SAAS,MAAM,SAAS,OAAO;AACrC,QAAM,OAAO,WAAW,UAAa,OAAO,SAAS,MAAM,MAAM,SAAS,QAAQ,cAAc,KAAK;AACrG,QAAM,UAAU,iBAAiB,QAAQ,CAAC,MAAM,OAAO,EAAE,SAAS,iBAAiB;AACnF,SAAO,CAAC,SAAS,GAAI,WAAW,CAAC,eAAe,IAAI,CAAC,GAAI,GAAI,UAAU,CAAC,iBAAiB,IAAI,CAAC,CAAE,EAAE,KAAK,MAAM;AAC/G;AAEA,SAAS,YACP,SACA,OACQ;AACR,MAAI,QAAQ,eAAe,OAAW,QAAO,kBAAkB,QAAQ,SAAS,KAAK;AACrF,QAAM,QAAQ,QAAQ,sBAAsB;AAC5C,SAAO,kBAAkB,MAAM,WAAW,UAAU,OAAO,OAAO,kBAAkB,OAAO,KAAK,CAAC;AACnG;AAEA,SAAS,aAAa,OAAqB,MAA6B;AACtE,QAAM,OAAO,MAAM,OAAO,MAAM,QAAQ,QAAQ,YAAY,EAAE;AAC9D,QAAM,UAAU,eAAe,OAAO,IAAI;AAC1C,QAAM,SAAS,aAAa,MAAM,IAAI,KAAK,MAAM;AACjD,SAAO,GAAG,IAAI,GAAG,UAAU,IAAI,OAAO,KAAK,EAAE,MAAM,WAAW,OAAO,IAAI,CAAC,MAAM,MAAM,SAAM,aAAa,KAAK,CAAC;AACjH;AAGO,SAAS,kBAAkB,MAAwB,SAAgB,oBAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE,GAAa;AACzH,QAAM,EAAE,KAAK,IAAI;AACjB,QAAM,MAAgB,CAAC;AACvB,MAAI,KAAK,SAAU,KAAI,KAAK,KAAK,UAAU,EAAE;AAC7C,MAAI,KAAK,WAAY,KAAI,KAAK,oBAAoB,KAAK,KAAK,2BAA2B,KAAK,UAAU,MAAM,EAAE;AAC9G,MAAI,KAAK,QAAQ,SAAS,GAAG;AAC3B,QAAI,KAAK,+BAA+B;AACxC,UAAM,UAAU,KAAK,IAAI,GAAG,KAAK,QAAQ,IAAI,CAAC,SAAS,KAAK,GAAG,MAAM,CAAC;AACtE,UAAM,aAAa,KAAK,IAAI,GAAG,KAAK,QAAQ,IAAI,CAAC,SAAS,KAAK,MAAM,MAAM,CAAC;AAC5E,eAAW,QAAQ,KAAK,SAAS;AAC/B,YAAM,SAAS,KAAK,UAAU,qBAAqB,KAAK,YAAY,QAAQ,KAAK,UAAW,KAAK,cAAc,KAAK;AACpH,UAAI,KAAK,KAAK,KAAK,GAAG,OAAO,OAAO,CAAC,KAAK,KAAK,MAAM,OAAO,UAAU,CAAC,MAAM,MAAM,EAAE;AAAA,IACvF;AACA,QAAI,KAAK,EAAE;AAAA,EACb;AACA,aAAW,QAAQ,KAAK,SAAS;AAC/B,UAAM,QAAQ,KAAK,QAAQ,IAAI,KAAK,OAAO;AAC3C,QAAI,CAAC,MAAO;AACZ,QAAI,KAAK,aAAa,OAAO,KAAK,IAAI,CAAC;AACvC,QAAI,KAAK,QAAS,KAAI,KAAK,KAAK,KAAK,OAAO,EAAE;AAC9C,UAAM,UAAU;AAAA,MACd,KAAK,UAAU,SAAS,IAAI,YAAY,KAAK,UAAU,KAAK,IAAI,CAAC,KAAK;AAAA,MACtE,YAAY,KAAK,WAAW,IAAI,iBAAiB,YAAY,KAAK,WAAW,CAAC,KAAK;AAAA,IACrF,EAAE,OAAO,CAAC,SAAyB,SAAS,IAAI;AAChD,QAAI,QAAQ,SAAS,EAAG,KAAI,KAAK,KAAK,QAAQ,KAAK,QAAK,CAAC,EAAE;AAC3D,QAAI,KAAK,mBAAmB,KAAK,kBAAkB,SAAS,KAAK,WAAW,YAAY;AACtF,UAAI,KAAK,uBAAuB,KAAK,eAAe,EAAE;AAAA,IACxD;AACA,QAAI,KAAK,cAAc,YAAY,MAAM,KAAK,CAAC,EAAE;AACjD,QAAI,KAAK,WAAW,KAAK,IAAI,EAAE;AAC/B,QAAI,KAAK,EAAE;AAAA,EACb;AACA,QAAM,aACJ,KAAK,SAAS,KAAK,KAAK,SAAS,OAC7B,KAAK,WAAW,IACd,+CAA+C,KAAK,IAAI,uBACxD,GAAG,KAAK,MAAM,gDAAgD,KAAK,IAAI,uBACzE;AACN,MAAI,KAAK,QAAQ,WAAW,KAAK,KAAK,QAAQ,WAAW,GAAG;AAC1D,UAAM,OAAO,KAAK,QAAQ,IAAI,KAAK,KAAK,MAAM;AAC9C,QAAI,KAAK,aAAa,0BAA0B,KAAK,IAAI,YAAY,IAAI,MAAM,sBAAsB,IAAI,GAAG;AAC5G,QAAI,WAAY,KAAI,KAAK,UAAU;AACnC,QAAI,KAAK,uBAAuB,gBAAgB,EAAE;AAAA,EACpD,WAAW,YAAY;AACrB,QAAI,KAAK,UAAU;AAAA,EACrB;AACA,SAAO,IAAI,SAAS,KAAK,IAAI,IAAI,SAAS,CAAC,MAAM,GAAI,KAAI,IAAI;AAC7D,SAAO;AACT;AAsBO,SAAS,cAAc,UAA2B,KAAa,KAAiB,OAA+B;AACpH,QAAM,iBAAiB,SAAS,SAAS,WAAW,CAAC;AACrD,QAAM,UAAU,SAAS,MAAM;AAC/B,QAAM,SAAS,WAAW,SAAS,MAAM,KAAK,GAAG,OAAO;AACxD,QAAM,OAAO,IAAI,YAAY,YAAY,IAAI,WAAW,SAAS,MAAM,KAAK,EAAE,OAAO;AACrF,QAAM,WAAW,WAAW,KAAK,SAAS,cAAc;AACxD,MAAI,SAAS,SAAS,WAAW;AAC/B,UAAM,aAAa,SAAS;AAC5B,WAAO;AAAA,MACL,MAAM;AAAA,QACJ,eAAe;AAAA,QACf;AAAA,QACA,OAAO;AAAA,QACP,YAAY,SAAS;AAAA,QACrB,SAAS;AAAA,QACT,UAAU,gBAAgB,YAAY,IAAI;AAAA,QAC1C,WAAW;AAAA,QACX,MAAM,QAAQ,UAAU,WAAW,IAAI;AAAA,MACzC;AAAA,MACA,SAAS;AAAA,MACT,gBAAgB,eAAe,WAAW,IAAI,IAAI,cAAc;AAAA,MAChE,OAAO;AAAA,IACT;AAAA,EACF;AACA,MAAI,SAAS,SAAS,QAAQ;AAC5B,UAAM,KAAK,aAAa,GAAG,EAAE,KAAK,KAAK,EAAE,YAAY;AACrD,UAAM,SAAS,QAAQ,OAAO,CAACA,WAAUA,OAAM,UAAU,SAAS,EAAE,CAAC;AACrE,QAAI,OAAO,SAAS,GAAG;AACrB,aAAO;AAAA,QACL,MAAM;AAAA,UACJ,eAAe;AAAA,UACf;AAAA,UACA,OAAO;AAAA,UACP,YAAY,SAAS;AAAA,UACrB,SAAS;AAAA,UACT,UAAU,CAAC,sBAAsB,IAAI,OAAO,MAAM,CAAC;AAAA,UACnD,WAAW;AAAA,UACX,MAAM;AAAA,QACR;AAAA,QACA,SAAS;AAAA,QACT,gBAAgB;AAAA,QAChB,OAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,MACL,MAAM,EAAE,eAAe,GAAG,OAAO,qBAAqB,KAAK,SAAS,UAAU,GAAG,YAAY,SAAS,WAAW;AAAA,MACjH,SAAS;AAAA,MACT,gBAAgB;AAAA,MAChB,OAAO;AAAA,IACT;AAAA,EACF;AACA,QAAM,QAAQ,SAAS;AACvB,SAAO;AAAA,IACL,MAAM;AAAA,MACJ,eAAe;AAAA,MACf;AAAA,MACA;AAAA,MACA,YAAY,CAAC;AAAA,MACb,SAAS;AAAA,MACT,UAAU,cAAc,OAAO,OAAO,MAAM,gBAAgB,QAAQ,CAAC;AAAA,MACrE,WAAW;AAAA,MACX,MAAM,QAAQ,UAAU,MAAM,MAAM,EAAE;AAAA,IACxC;AAAA,IACA,SAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,OAAO;AAAA,EACT;AACF;AAEA,SAAS,OAAO,OAAe,KAAa,MAAsB;AAChE,SAAO,GAAG,KAAK,IAAI,UAAU,IAAI,MAAM,IAAI;AAC7C;AAEA,SAAS,YAAY,OAAoC;AACvD,QAAM,QAAQ,MAAM,SAAS;AAC7B,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,QAAQ;AAAA,IACZ,MAAM,UAAU,MAAM,OAAO,SAAS,IAAI,OAAO,MAAM,OAAO,QAAQ,SAAS,QAAQ,IAAI;AAAA,IAC3F,MAAM,eAAe,MAAM,YAAY,SAAS,IAAI,OAAO,MAAM,YAAY,QAAQ,cAAc,aAAa,IAAI;AAAA,IACpH,MAAM,iBAAiB,MAAM,cAAc,SAAS,IAAI,SAAS,OAAO,MAAM,cAAc,QAAQ,SAAS,QAAQ,CAAC,KAAK;AAAA,IAC3H,MAAM,gBAAgB,MAAM,aAAa,SAAS,IAC9C,OAAO,MAAM,aAAa,QAAQ,gBAAgB,eAAe,IACjE;AAAA,EACN,EAAE,OAAO,CAAC,SAAyB,SAAS,IAAI;AAChD,SAAO,MAAM,SAAS,IAAI,MAAM,KAAK,QAAK,IAAI;AAChD;AAEO,SAAS,gBAAgB,MAAsB,UAAqC;AACzF,QAAM,EAAE,KAAK,IAAI;AACjB,MAAI,WAAW,KAAM,QAAO,CAAC,KAAK,KAAK;AACvC,QAAM,MAAgB,CAAC;AACvB,MAAI,KAAK,SAAS;AAChB,UAAM,aAAa,KAAK;AACxB,QAAI,KAAK,GAAG,WAAW,EAAE,qCAAqC,WAAW,KAAK,GAAG;AACjF,QAAI,WAAW,YAAa,KAAI,KAAK,KAAK,WAAW,WAAW,EAAE;AAClE,UAAM,aAAa,KAAK,mBAAmB,QAAQ,OAAO,kBAAkB,YAAY,KAAK,IAAI;AACjG,QAAI,KAAK,eAAgB,KAAI,KAAK,oBAAoB;AAAA,aAC7C,WAAY,KAAI,KAAK,KAAK,UAAU,EAAE;AAAA,QAC1C,KAAI,KAAK,aAAa,WAAW,WAAW,qBAAqB,WAAW,EAAE,CAAC,EAAE;AACtF,QAAI,KAAK,WAAW,SAAS,EAAG,KAAI,KAAK,uCAAuC,KAAK,WAAW,KAAK,IAAI,CAAC,EAAE;AAC5G,QAAI,KAAK,KAAM,KAAI,KAAK,WAAW,KAAK,IAAI,EAAE;AAC9C,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,KAAK;AACnB,MAAI,CAAC,OAAO;AACV,QAAI,KAAK,GAAG,KAAK,QAAQ;AACzB,QAAI,KAAK,WAAW,SAAS,EAAG,KAAI,KAAK,eAAe,KAAK,WAAW,KAAK,IAAI,CAAC,EAAE;AACpF,WAAO;AAAA,EACT;AACA,QAAM,OAAO,KAAK;AAClB,MAAI,KAAK,aAAa,OAAO,IAAI,CAAC;AAClC,MAAI,KAAK,KAAK,MAAM,KAAK,KAAK,MAAM,OAAO,EAAE;AAC7C,MAAI,MAAM,UAAW,KAAI,KAAK,KAAK,MAAM,SAAS,EAAE;AACpD,QAAM,QAAQ,SAAS,MAAM,MAAM,IAAI,CAAC,SAAS,GAAG,KAAK,GAAG,IAAI,SAAS,OAAO,KAAK,GAAG,CAAC,EAAE;AAC3F,MAAI,MAAM,SAAS,EAAG,KAAI,KAAK,YAAY,MAAM,KAAK,QAAK,CAAC,EAAE;AAC9D,QAAM,SAAS,OAAQ,MAAM,YAAY,IAAI,KAAK,OAAQ;AAC1D,MAAI,UAAU,MAAM;AAClB,QAAI,KAAK,qBAAqB,IAAI,KAAK,OAAO,QAAQ,EAAE;AACxD,eAAW,UAAU,OAAO,QAAQ,MAAM,GAAG,CAAC,EAAG,KAAI,KAAK,OAAO,OAAO,IAAI,KAAK,OAAO,IAAI,EAAE;AAAA,EAChG;AACA,QAAM,OAAO,YAAY,KAAK;AAC9B,MAAI,KAAM,KAAI,KAAK,WAAW,IAAI,EAAE;AACpC,QAAM,QAAQ,MAAM,SAAS;AAC7B,QAAM,WAAW,CAAC,GAAI,OAAO,OAAO,CAAC,GAAI,IAAI,OAAO,eAAe,CAAC,GAAG,IAAI,CAAC,eAAe,WAAW,GAAG,CAAC;AAC1G,MAAI,SAAS,SAAS,EAAG,KAAI,KAAK,qBAAqB,SAAS,MAAM,GAAG,EAAE,EAAE,KAAK,IAAI,CAAC,EAAE;AACzF,MAAI,OAAO,SAAS,MAAM,MAAM,SAAS,EAAG,KAAI,KAAK,eAAe,MAAM,MAAM,MAAM,GAAG,EAAE,EAAE,KAAK,IAAI,CAAC,EAAE;AACzG,QAAM,UAAU,cAAc,OAAO,MAAM,WAAW,SAAS,MAAM,KAAK,GAAG,OAAO,MAAM,WAAW,QAAQ,CAAC;AAC9G,MAAI,KAAK,cAAc,YAAY,SAAS,KAAK,CAAC,EAAE;AACpD,aAAW,WAAW,KAAK,SAAU,KAAI,KAAK,cAAc,OAAO,EAAE;AACrE,MAAI,KAAK,mBAAmB,KAAK,QAAQ,MAAM,MAAM,EAAE,EAAE;AACzD,MAAI,KAAK,WAAW,QAAQ,UAAU,MAAM,MAAM,IAAI,CAAC,EAAE;AACzD,SAAO;AACT;AAYA,MAAM,eACJ;AACF,MAAM,aAAa;AAEZ,MAAM,+BACX;AAIF,eAAe,eAAe,MAAmD;AAC/E,QAAM,MAAM,KAAK,OAAO,QAAQ;AAChC,QAAM,UAAU,KAAK,WAAW,eAAe,EAAE,UAAU;AAC3D,QAAM,SAAS,KAAK,WAAW,CAAC,SAAiB,QAAQ,MAAM,IAAI;AACnE,QAAM,MAAM,IAAI,sBAAsB,KAAK,KAAK;AAChD,QAAM,aAAa,IAAI,wBAAwB,KAAK,KAAK;AACzD,QAAM,SAAS,MAAM,UAAU;AAAA,IAC7B;AAAA,IACA;AAAA,IACA,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,IACnC,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,UAAU,IAAI,CAAC;AAAA,IACtD,GAAI,KAAK,MAAM,EAAE,KAAK,KAAK,IAAI,IAAI,CAAC;AAAA,EACtC,CAAC;AACD,MAAI,OAAO,WAAW,cAAe,QAAO,kCAAkC,OAAO,SAAS,GAAG;AACjG,MAAI,OAAO,uBAAuB,MAAM;AACtC,WAAO,4BAA4B;AACnC,WAAO,WAAW,OAAO,kBAAkB,EAAE;AAAA,EAC/C;AACA,QAAM,MAAM,iBAAiB,SAAS,KAAK,sBAAsB,MAAM,GAAG;AAC1E,SAAO;AAAA,IACL,UAAU,OAAO;AAAA,IACjB;AAAA,IACA,oBAAoB,OAAO,uBAAuB;AAAA,EACpD;AACF;AAEA,SAAS,gBAAgB,OAAgB,QAAwC;AAC/E,MAAI,iBAAiB,6BAA6B;AAChD,WAAO,gCAAgC;AACvC,WAAO,WAAW,MAAM,MAAM,EAAE;AAChC,WAAO;AAAA,EACT;AACA,QAAM;AACR;AAEA,eAAsB,gBAAgB,MAAgB,OAAyB,CAAC,GAAoB;AAClG,QAAM,SAAS,KAAK,WAAW,CAAC,SAAiB,QAAQ,IAAI,IAAI;AACjE,QAAM,SAAS,KAAK,WAAW,CAAC,SAAiB,QAAQ,MAAM,IAAI;AACnE,QAAM,SAAS,sBAAsB,IAAI;AACzC,MAAI,CAAC,OAAO,SAAS,CAAC,OAAO,YAAY,CAAC,OAAO,OAAO;AACtD,WAAO,YAAY;AACnB,WAAO;AAAA,EACT;AACA,MAAI;AACJ,MAAI;AACF,aAAS,MAAM,eAAe,IAAI;AAAA,EACpC,SAAS,OAAgB;AACvB,WAAO,gBAAgB,OAAO,MAAM;AAAA,EACtC;AACA,QAAM,OAAO;AAAA,IACX,OAAO;AAAA,IACP,EAAE,OAAO,OAAO,OAAO,UAAU,OAAO,UAAU,OAAO,OAAO,OAAO,UAAU,OAAO,UAAU,OAAO,OAAO,MAAM;AAAA,IACtH,OAAO;AAAA,EACT;AACA,MAAI,OAAO,mBAAoB,MAAK,KAAK,qBAAqB;AAC9D,QAAM,QAAQ,IAAI,KAAK,KAAK,MAAM,KAAK,IAAI,IAAI,KAAK,IAAI,CAAC,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AACpF,MAAI,OAAO,KAAM,QAAO,KAAK,UAAU,KAAK,MAAM,MAAM,CAAC,CAAC;AAAA,MACrD,YAAW,QAAQ,kBAAkB,MAAM,KAAK,EAAG,QAAO,IAAI;AACnE,SAAO;AACT;AAEA,eAAsB,cAAc,MAAgB,OAAyB,CAAC,GAAoB;AAChG,QAAM,SAAS,KAAK,WAAW,CAAC,SAAiB,QAAQ,IAAI,IAAI;AACjE,QAAM,SAAS,KAAK,WAAW,CAAC,SAAiB,QAAQ,MAAM,IAAI;AACnE,QAAM,SAAS,oBAAoB,IAAI;AACvC,MAAI,CAAC,OAAO,KAAK;AACf,WAAO,UAAU;AACjB,WAAO;AAAA,EACT;AACA,MAAI;AACJ,MAAI;AACF,aAAS,MAAM,eAAe,IAAI;AAAA,EACpC,SAAS,OAAgB;AACvB,WAAO,gBAAgB,OAAO,MAAM;AAAA,EACtC;AACA,QAAM,QAAQ,IAAI,KAAK,KAAK,MAAM,KAAK,IAAI,IAAI,KAAK,IAAI,CAAC,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AACpF,QAAM,OAAO,cAAc,OAAO,UAAU,OAAO,KAAK,OAAO,KAAK,KAAK;AACzE,MAAI,OAAO,KAAM,QAAO,KAAK,UAAU,KAAK,MAAM,MAAM,CAAC,CAAC;AAAA,WACjD,KAAK,MAAO,YAAW,QAAQ,gBAAgB,MAAM,OAAO,QAAQ,EAAG,QAAO,IAAI;AAAA,MACtF,YAAW,QAAQ,gBAAgB,MAAM,OAAO,QAAQ,EAAG,QAAO,IAAI;AAC3E,SAAO,KAAK,QAAQ,IAAI;AAC1B;",
6
6
  "names": ["entry"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/cli",
3
- "version": "0.10.1-develop.7803.1.e99e862b4e",
3
+ "version": "0.10.1-develop.7807.1.9b2b8953e9",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -60,10 +60,10 @@
60
60
  "@mikro-orm/decorators": "^7.1.14",
61
61
  "@mikro-orm/migrations": "^7.1.14",
62
62
  "@mikro-orm/postgresql": "^7.1.14",
63
- "@open-mercato/events": "0.10.1-develop.7803.1.e99e862b4e",
64
- "@open-mercato/queue": "0.10.1-develop.7803.1.e99e862b4e",
65
- "@open-mercato/shared": "0.10.1-develop.7803.1.e99e862b4e",
66
- "@open-mercato/telemetry": "0.10.1-develop.7803.1.e99e862b4e",
63
+ "@open-mercato/events": "0.10.1-develop.7807.1.9b2b8953e9",
64
+ "@open-mercato/queue": "0.10.1-develop.7807.1.9b2b8953e9",
65
+ "@open-mercato/shared": "0.10.1-develop.7807.1.9b2b8953e9",
66
+ "@open-mercato/telemetry": "0.10.1-develop.7807.1.9b2b8953e9",
67
67
  "cross-spawn": "^7.0.6",
68
68
  "minisearch": "7.2.0",
69
69
  "pg": "8.23.0",
@@ -75,10 +75,10 @@
75
75
  "typescript-js": "npm:typescript@6.0.3"
76
76
  },
77
77
  "peerDependencies": {
78
- "@open-mercato/shared": "0.10.1-develop.7803.1.e99e862b4e"
78
+ "@open-mercato/shared": "0.10.1-develop.7807.1.9b2b8953e9"
79
79
  },
80
80
  "devDependencies": {
81
- "@open-mercato/shared": "0.10.1-develop.7803.1.e99e862b4e",
81
+ "@open-mercato/shared": "0.10.1-develop.7807.1.9b2b8953e9",
82
82
  "@types/jest": "^30.0.0",
83
83
  "jest": "^30.4.2",
84
84
  "ts-jest": "^29.4.12"
@@ -20,6 +20,7 @@ import {
20
20
  defaultBuiltInUrl,
21
21
  loadIndex,
22
22
  MODULE_INDEX_CACHE_FILE,
23
+ MODULE_INDEX_MAX_BYTES,
23
24
  MODULE_INDEX_UNAVAILABLE_EXIT_CODE,
24
25
  MODULE_INDEX_UNAVAILABLE_MESSAGE,
25
26
  BUILT_IN_UNAVAILABLE_MESSAGE,
@@ -121,6 +122,7 @@ function jsonResponse(status: number, body: unknown, etag: string | null = null)
121
122
  status,
122
123
  ok: status >= 200 && status < 300,
123
124
  headers: { get: (name: string) => (name.toLowerCase() === 'etag' ? etag : null) },
125
+ body: status === 304 ? null : new Response(JSON.stringify(body)).body,
124
126
  json: async () => body,
125
127
  }
126
128
  }
@@ -278,6 +280,19 @@ describe('resolveLine', () => {
278
280
  })
279
281
  })
280
282
 
283
+ describe('module search catalog version forms', () => {
284
+ it.each([
285
+ '0.10.1-develop.1+build', '0.9.0+build', '0.9.0-rc.01', '0.10.1-develop.01',
286
+ '00.9.0', '0.09.0', '0.9.00', '0.9.0-rc..1', '0.9.0-rc.', '0.9-rc.1',
287
+ ])('rejects non-catalog version %s', (version) => {
288
+ expect(resolveLine(version, index.lines)).toEqual({ line: null, version: null, note: 'Unknown Open Mercato version.' })
289
+ })
290
+
291
+ it.each(['0.9.0-rc.0', '0.9.0-rc.1a', '~0.9.2', 'v0.9.0'])('keeps valid version %s', (version) => {
292
+ expect(resolveLine(version, index.lines).line).toBe('0.9')
293
+ })
294
+ })
295
+
281
296
  describe('module search visibility regressions', () => {
282
297
  function entry(locator: string, title: string, verified: boolean, kind: CatalogEntry['kind'] = 'module'): CatalogEntry {
283
298
  const base = structuredClone(index.entries[0])
@@ -705,6 +720,102 @@ describe('module search app-root resolution', () => {
705
720
  })
706
721
  })
707
722
 
723
+ describe('module index transport bounds', () => {
724
+ it.each(['http://catalog.example/modules.json', 'ftp://localhost/modules.json', 'http://localhost.example/modules.json'])('rejects unsafe URL %s before fetching', async (url) => {
725
+ const fetchImpl = jest.fn(fixtureFetch().fetchImpl)
726
+ await expect(loadIndex({ appRoot: tempApp(), url, fetchImpl })).rejects.toThrow('HTTPS')
727
+ expect(fetchImpl).not.toHaveBeenCalled()
728
+ })
729
+
730
+ it.each(['localhost', '127.0.0.1', '[::1]'])('allows local HTTP on %s', async (host) => {
731
+ const loaded = await loadIndex({ appRoot: tempApp(), url: `http://${host}/modules.json`, fetchImpl: fixtureFetch().fetchImpl })
732
+ expect(loaded.source).toBe('network')
733
+ })
734
+
735
+ it('validates the built-in override too', async () => {
736
+ const fetchImpl = jest.fn(fixtureFetch().fetchImpl)
737
+ await expect(loadIndex({ appRoot: tempApp(), builtInUrl: 'http://remote.example/built-in.json', fetchImpl })).rejects.toThrow('HTTPS')
738
+ expect(fetchImpl).not.toHaveBeenCalled()
739
+ })
740
+
741
+ it('refuses HTTPS downgrades before requesting the redirect target', async () => {
742
+ const fetchImpl = jest.fn<ReturnType<FetchLike>, Parameters<FetchLike>>(async () => new Response(null, {
743
+ status: 302, headers: { location: 'http://remote.example/modules.json' },
744
+ }))
745
+ await expect(loadIndex({ appRoot: tempApp(), fetchImpl })).rejects.toThrow('HTTPS')
746
+ expect(fetchImpl.mock.calls.every(([url, init]) => url === DEFAULT_MODULES_INDEX_URL && init?.redirect === 'manual')).toBe(true)
747
+ })
748
+
749
+ it('follows HTTPS redirects', async () => {
750
+ const fixtures = fixtureFetch().fetchImpl
751
+ const fetchImpl: FetchLike = async (url, init) => url === DEFAULT_MODULES_INDEX_URL
752
+ ? new Response(null, { status: 302, headers: { location: 'https://mirror.example/modules.json' } })
753
+ : fixtures(url, init)
754
+ expect((await loadIndex({ appRoot: tempApp(), fetchImpl })).source).toBe('network')
755
+ })
756
+
757
+ it('rejects oversized Content-Length before consuming the body', async () => {
758
+ const cancel = jest.fn()
759
+ const fetchImpl: FetchLike = async () => new Response(new ReadableStream({ cancel }), {
760
+ headers: { 'content-length': String(MODULE_INDEX_MAX_BYTES + 1) },
761
+ })
762
+ await expect(loadIndex({ appRoot: tempApp(), fetchImpl })).rejects.toThrow('exceeds')
763
+ expect(cancel).toHaveBeenCalledTimes(1)
764
+ })
765
+
766
+ it.each([undefined, '1'])('caps streamed bytes regardless of Content-Length (%s), cancels and keeps stale cache', async (length) => {
767
+ const app = tempApp()
768
+ await loadIndex({ appRoot: app, fetchImpl: fixtureFetch().fetchImpl, now: () => NOW })
769
+ const cache = fs.readFileSync(path.join(app, MODULE_INDEX_CACHE_FILE), 'utf8')
770
+ const cancel = jest.fn()
771
+ const fetchImpl: FetchLike = async () => new Response(new ReadableStream({
772
+ start(controller) {
773
+ controller.enqueue(new Uint8Array(MODULE_INDEX_MAX_BYTES))
774
+ controller.enqueue(new Uint8Array(1))
775
+ },
776
+ cancel,
777
+ }), { headers: length ? { 'content-length': length } : {} })
778
+ const loaded = await loadIndex({ appRoot: app, fetchImpl, now: () => NOW + 60 * 60 * 1000 })
779
+ expect(loaded.source).toBe('stale-cache')
780
+ expect(cancel).toHaveBeenCalledTimes(1)
781
+ expect(fs.readFileSync(path.join(app, MODULE_INDEX_CACHE_FILE), 'utf8')).toBe(cache)
782
+ })
783
+
784
+ it('accepts a streamed index exactly at the size limit', async () => {
785
+ const serialized = JSON.stringify(index)
786
+ const padded = serialized + ' '.repeat(MODULE_INDEX_MAX_BYTES - Buffer.byteLength(serialized))
787
+ const fetchImpl: FetchLike = async (url) => url.endsWith('/modules.json')
788
+ ? new Response(padded)
789
+ : jsonResponse(200, builtIn)
790
+ expect((await loadIndex({ appRoot: tempApp(), fetchImpl })).source).toBe('network')
791
+ })
792
+
793
+ it('bounds redirect loops', async () => {
794
+ const fetchImpl = jest.fn<ReturnType<FetchLike>, Parameters<FetchLike>>(async () => new Response(null, {
795
+ status: 302, headers: { location: DEFAULT_MODULES_INDEX_URL },
796
+ }))
797
+ await expect(loadIndex({ appRoot: tempApp(), fetchImpl })).rejects.toThrow('redirects')
798
+ expect(fetchImpl).toHaveBeenCalledTimes(12)
799
+ })
800
+
801
+ it('reports an unsafe override through the normal unavailable exit code', async () => {
802
+ const io = capture()
803
+ const options = { ...deps(tempApp(), fixtureFetch().fetchImpl, io), env: { OM_MODULES_INDEX_URL: 'http://remote.example/modules.json' } }
804
+ expect(await runModuleSearch(['stripe'], options)).toBe(MODULE_INDEX_UNAVAILABLE_EXIT_CODE)
805
+ expect(io.stderr.join(' ')).toContain('HTTPS')
806
+ expect(io.stdout).toEqual([])
807
+ })
808
+
809
+ it('applies the cap to the built-in index', async () => {
810
+ const fetchImpl: FetchLike = async (url) => url.endsWith('/modules.json')
811
+ ? jsonResponse(200, index)
812
+ : new Response('too large', { headers: { 'content-length': String(MODULE_INDEX_MAX_BYTES + 1) } })
813
+ const loaded = await loadIndex({ appRoot: tempApp(), fetchImpl })
814
+ expect(loaded.builtIn).toBeNull()
815
+ expect(loaded.builtInUnavailable).toContain('exceeds')
816
+ })
817
+ })
818
+
708
819
  describe('module index download and offline cache', () => {
709
820
  it('downloads modules.json and built-in.json from modules.openmercato.com and caches them', async () => {
710
821
  const app = tempApp()
@@ -385,7 +385,7 @@ export function latestLine(lines: readonly CatalogLine[]): CatalogLine | null {
385
385
  return lines.find((line) => line.source === 'latest') ?? null
386
386
  }
387
387
 
388
- const SEMVER = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/
388
+ const SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/
389
389
 
390
390
  export function resolveLine(
391
391
  omVersion: string | null | undefined,
@@ -398,14 +398,18 @@ export function resolveLine(
398
398
  const byKey = lines.find((line) => line.key === raw)
399
399
  if (byKey) return found(byKey)
400
400
  const version = raw.replace(/^(?:v|=|\^|~|>=)\s*/, '')
401
+ const semver = SEMVER.exec(version)
402
+ const lineKey = /^(0|[1-9]\d*)\.(0|[1-9]\d*)$/.exec(version)
403
+ if ((!semver && !lineKey) || semver?.[4]?.split('.').some((part) => /^0\d+$/.test(part))) {
404
+ return { line: null, version: null, note: 'Unknown Open Mercato version.' }
405
+ }
401
406
  const exact = lines.find((line) => line.version === version)
402
407
  if (exact) return found(exact)
403
- const semver = SEMVER.exec(version)
404
408
  if (semver && semver[4] && /(^|[.-])develop([.-]|$)/.test(semver[4])) {
405
409
  const next = lines.find((line) => line.source === 'develop')
406
410
  if (next) return found(next)
407
411
  }
408
- const majorMinor = /^(\d+)\.(\d+)(?:\.\d+)?(?:-[0-9A-Za-z.-]+)?$/.exec(version)
412
+ const majorMinor = semver ?? lineKey
409
413
  if (!majorMinor) return { line: null, version: null, note: 'Unknown Open Mercato version.' }
410
414
  const key = `${Number(majorMinor[1])}.${Number(majorMinor[2])}`
411
415
  const line = lines.find((candidate) => candidate.key === key)
@@ -40,6 +40,7 @@ import { parseModuleInfoArgs, parseModuleSearchArgs } from './module-search-args
40
40
 
41
41
  export const DEFAULT_MODULES_INDEX_URL = 'https://modules.openmercato.com/modules.json'
42
42
  export const MODULES_SITE_URL = 'https://modules.openmercato.com'
43
+ export const MODULE_INDEX_MAX_BYTES = 10 * 1024 * 1024
43
44
  export const MODULE_INDEX_TIMEOUT_MS = 4000
44
45
  export const MODULE_INDEX_TTL_MS = 10 * 60 * 1000
45
46
  export const MODULE_INDEX_CACHE_FILE = path.join('.mercato', 'cache', 'modules-index.json')
@@ -51,12 +52,13 @@ export type FetchResponseLike = {
51
52
  status: number
52
53
  ok: boolean
53
54
  headers: { get(name: string): string | null }
55
+ body?: ReadableStream<Uint8Array> | null
54
56
  json(): Promise<unknown>
55
57
  }
56
58
 
57
59
  export type FetchLike = (
58
60
  input: string,
59
- init?: { headers?: Record<string, string>; signal?: AbortSignal },
61
+ init?: { headers?: Record<string, string>; signal?: AbortSignal; redirect?: 'manual' },
60
62
  ) => Promise<FetchResponseLike>
61
63
 
62
64
  export type LoadedModuleIndex = {
@@ -313,6 +315,14 @@ function writeCache(file: string, cache: CacheFile): void {
313
315
  }
314
316
  }
315
317
 
318
+ function validateIndexUrl(value: string): void {
319
+ const url = new URL(value)
320
+ const local = url.hostname === 'localhost' || url.hostname === '127.0.0.1' || url.hostname === '[::1]'
321
+ if (url.protocol !== 'https:' && !(url.protocol === 'http:' && local)) {
322
+ throw new Error('[internal] Module index URL must use HTTPS except on loopback hosts')
323
+ }
324
+ }
325
+
316
326
  async function fetchOnce(
317
327
  fetchImpl: FetchLike,
318
328
  url: string,
@@ -321,7 +331,50 @@ async function fetchOnce(
321
331
  ): Promise<FetchResponseLike> {
322
332
  const headers: Record<string, string> = { accept: 'application/json' }
323
333
  if (etag) headers['if-none-match'] = etag
324
- return fetchImpl(url, { headers, signal: AbortSignal.timeout(timeoutMs) })
334
+ const signal = AbortSignal.timeout(timeoutMs)
335
+ let target = url
336
+ for (let redirects = 0; redirects <= 5; redirects += 1) {
337
+ validateIndexUrl(target)
338
+ const response = await fetchImpl(target, { headers, signal, redirect: 'manual' })
339
+ if (![301, 302, 303, 307, 308].includes(response.status)) return response
340
+ await response.body?.cancel()
341
+ const location = response.headers.get('location')
342
+ if (!location || redirects === 5) throw new Error('[internal] Invalid or excessive module index redirects')
343
+ target = new URL(location, target).toString()
344
+ }
345
+ throw new Error('[internal] Excessive module index redirects')
346
+ }
347
+
348
+ async function readBoundedJson(response: FetchResponseLike): Promise<unknown> {
349
+ const tooLarge = () => new Error(`[internal] Module index exceeds ${MODULE_INDEX_MAX_BYTES} bytes`)
350
+ const length = Number(response.headers.get('content-length'))
351
+ if (length > MODULE_INDEX_MAX_BYTES) {
352
+ await response.body?.cancel()
353
+ throw tooLarge()
354
+ }
355
+ if (!response.body) {
356
+ const value = await response.json()
357
+ if (Buffer.byteLength(JSON.stringify(value) ?? '') > MODULE_INDEX_MAX_BYTES) throw tooLarge()
358
+ return value
359
+ }
360
+ const reader = response.body.getReader()
361
+ const chunks: Uint8Array[] = []
362
+ let bytes = 0
363
+ try {
364
+ while (true) {
365
+ const chunk = await reader.read()
366
+ if (chunk.done) break
367
+ bytes += chunk.value.byteLength
368
+ if (bytes > MODULE_INDEX_MAX_BYTES) {
369
+ await reader.cancel()
370
+ throw tooLarge()
371
+ }
372
+ chunks.push(chunk.value)
373
+ }
374
+ return JSON.parse(Buffer.concat(chunks).toString('utf8'))
375
+ } finally {
376
+ reader.releaseLock()
377
+ }
325
378
  }
326
379
 
327
380
  async function fetchWithRetry(
@@ -335,6 +388,7 @@ async function fetchWithRetry(
335
388
  try {
336
389
  const response = await fetchOnce(fetchImpl, url, etag, timeoutMs)
337
390
  if (response.status >= 500) {
391
+ await response.body?.cancel()
338
392
  lastError = new Error(`${url} answered ${response.status}`)
339
393
  continue
340
394
  }
@@ -356,7 +410,7 @@ async function fetchJson<T>(
356
410
  const response = await fetchWithRetry(fetchImpl, url, etag, timeoutMs)
357
411
  if (response.status === 304) return { notModified: true }
358
412
  if (!response.ok) throw new Error(`${url} answered ${response.status}`)
359
- const value = parse(await response.json())
413
+ const value = parse(await readBoundedJson(response))
360
414
  if (value === null) throw new Error(`${url} is not a module index this CLI understands`)
361
415
  return { notModified: false, value, etag: response.headers.get('etag') }
362
416
  }
@@ -371,7 +425,14 @@ function searcherOf(index: CatalogIndex, builtIn: BuiltInIndex | null): CatalogS
371
425
 
372
426
  export async function loadIndex(options: LoadIndexOptions): Promise<LoadedModuleIndex> {
373
427
  const url = options.url ?? DEFAULT_MODULES_INDEX_URL
374
- const builtInUrl = options.builtInUrl ?? defaultBuiltInUrl(url)
428
+ let builtInUrl: string
429
+ try {
430
+ validateIndexUrl(url)
431
+ builtInUrl = options.builtInUrl ?? defaultBuiltInUrl(url)
432
+ validateIndexUrl(builtInUrl)
433
+ } catch (error: unknown) {
434
+ throw new ModuleIndexUnavailableError(error instanceof Error ? error.message : String(error))
435
+ }
375
436
  const timeoutMs = options.timeoutMs ?? MODULE_INDEX_TIMEOUT_MS
376
437
  const ttlMs = options.ttlMs ?? MODULE_INDEX_TTL_MS
377
438
  const fetchImpl: FetchLike = options.fetchImpl ?? ((input, init) => fetch(input, init))