@open-mercato/cli 0.10.1-develop.7802.1.2697b2d029 → 0.10.1-develop.7804.1.347c813134
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agentic/guides/module-facts.json +162 -162
- package/dist/agentic/guides/module-facts.v2.json +162 -162
- package/dist/agentic/guides/modules/agent_orchestrator/index.md +1 -1
- package/dist/agentic/guides/modules/ai_assistant/index.md +1 -1
- package/dist/agentic/guides/modules/api_docs/index.md +1 -1
- package/dist/agentic/guides/modules/api_keys/index.md +1 -1
- package/dist/agentic/guides/modules/attachments/index.md +1 -1
- package/dist/agentic/guides/modules/audit_logs/index.md +1 -1
- package/dist/agentic/guides/modules/auth/index.md +1 -1
- package/dist/agentic/guides/modules/availability/index.md +1 -1
- package/dist/agentic/guides/modules/business_rules/index.md +1 -1
- package/dist/agentic/guides/modules/carrier_inpost/index.md +1 -1
- package/dist/agentic/guides/modules/cart/index.md +1 -1
- package/dist/agentic/guides/modules/catalog/index.md +1 -1
- package/dist/agentic/guides/modules/channel_apns/index.md +1 -1
- package/dist/agentic/guides/modules/channel_discord/index.md +1 -1
- package/dist/agentic/guides/modules/channel_expo/index.md +1 -1
- package/dist/agentic/guides/modules/channel_fcm/index.md +1 -1
- package/dist/agentic/guides/modules/channel_gmail/index.md +1 -1
- package/dist/agentic/guides/modules/channel_imap/index.md +1 -1
- package/dist/agentic/guides/modules/channel_resend/index.md +1 -1
- package/dist/agentic/guides/modules/channel_ses/index.md +1 -1
- package/dist/agentic/guides/modules/channel_smtp/index.md +1 -1
- package/dist/agentic/guides/modules/checkout/index.md +1 -1
- package/dist/agentic/guides/modules/communication_channels/index.md +1 -1
- package/dist/agentic/guides/modules/configs/index.md +1 -1
- package/dist/agentic/guides/modules/content/index.md +1 -1
- package/dist/agentic/guides/modules/currencies/index.md +1 -1
- package/dist/agentic/guides/modules/customer_accounts/index.md +1 -1
- package/dist/agentic/guides/modules/customer_groups/index.md +1 -1
- package/dist/agentic/guides/modules/customers/index.md +1 -1
- package/dist/agentic/guides/modules/dashboards/index.md +1 -1
- package/dist/agentic/guides/modules/data_sync/index.md +1 -1
- package/dist/agentic/guides/modules/design_system/index.md +1 -1
- package/dist/agentic/guides/modules/devices/index.md +1 -1
- package/dist/agentic/guides/modules/dictionaries/index.md +1 -1
- package/dist/agentic/guides/modules/directory/index.md +1 -1
- package/dist/agentic/guides/modules/documents/index.md +1 -1
- package/dist/agentic/guides/modules/ecommerce/index.md +1 -1
- package/dist/agentic/guides/modules/entities/index.md +1 -1
- package/dist/agentic/guides/modules/eudr/index.md +1 -1
- package/dist/agentic/guides/modules/events/index.md +1 -1
- package/dist/agentic/guides/modules/feature_toggles/index.md +1 -1
- package/dist/agentic/guides/modules/forms/index.md +1 -1
- package/dist/agentic/guides/modules/gateway_autopay/index.md +1 -1
- package/dist/agentic/guides/modules/gateway_stripe/index.md +1 -1
- package/dist/agentic/guides/modules/gateway_tpay/index.md +1 -1
- package/dist/agentic/guides/modules/generators/index.md +1 -1
- package/dist/agentic/guides/modules/inbox_ops/index.md +1 -1
- package/dist/agentic/guides/modules/integrations/index.md +1 -1
- package/dist/agentic/guides/modules/messages/index.md +1 -1
- package/dist/agentic/guides/modules/notifications/index.md +1 -1
- package/dist/agentic/guides/modules/onboarding/index.md +1 -1
- package/dist/agentic/guides/modules/payment_gateways/index.md +1 -1
- package/dist/agentic/guides/modules/perspectives/index.md +1 -1
- package/dist/agentic/guides/modules/phone_calls/index.md +1 -1
- package/dist/agentic/guides/modules/planner/index.md +1 -1
- package/dist/agentic/guides/modules/portal/index.md +1 -1
- package/dist/agentic/guides/modules/progress/index.md +1 -1
- package/dist/agentic/guides/modules/promotions/index.md +1 -1
- package/dist/agentic/guides/modules/push_notifications/index.md +1 -1
- package/dist/agentic/guides/modules/query_index/index.md +1 -1
- package/dist/agentic/guides/modules/record_locks/index.md +1 -1
- package/dist/agentic/guides/modules/resources/index.md +1 -1
- package/dist/agentic/guides/modules/sales/index.md +1 -1
- package/dist/agentic/guides/modules/scheduler/index.md +1 -1
- package/dist/agentic/guides/modules/search/index.md +1 -1
- package/dist/agentic/guides/modules/security/index.md +1 -1
- package/dist/agentic/guides/modules/seeds/index.md +1 -1
- package/dist/agentic/guides/modules/shipping_carriers/index.md +1 -1
- package/dist/agentic/guides/modules/sso/index.md +1 -1
- package/dist/agentic/guides/modules/staff/index.md +1 -1
- package/dist/agentic/guides/modules/storage_s3/index.md +1 -1
- package/dist/agentic/guides/modules/sync_akeneo/index.md +1 -1
- package/dist/agentic/guides/modules/sync_excel/index.md +1 -1
- package/dist/agentic/guides/modules/system_status_overlays/index.md +1 -1
- package/dist/agentic/guides/modules/telemetry/index.md +1 -1
- package/dist/agentic/guides/modules/tillio/index.md +1 -1
- package/dist/agentic/guides/modules/translations/index.md +1 -1
- package/dist/agentic/guides/modules/warranty_claims/index.md +1 -1
- package/dist/agentic/guides/modules/webhooks/index.md +1 -1
- package/dist/agentic/guides/modules/wms/index.md +1 -1
- package/dist/agentic/guides/modules/workflows/index.md +1 -1
- package/dist/agentic/guides/reference-module-facts.json +1 -1
- package/dist/agentic/guides/upstream/manifest.json +1 -1
- package/dist/lib/module-search-index.js +7 -3
- package/dist/lib/module-search-index.js.map +2 -2
- package/dist/lib/module-search.js +62 -3
- package/dist/lib/module-search.js.map +2 -2
- package/package.json +7 -7
- package/src/lib/__tests__/module-search.test.ts +111 -0
- package/src/lib/module-search-index.ts +7 -3
- 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": "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",
|
|
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": "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",
|
|
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.
|
|
3
|
+
"version": "0.10.1-develop.7804.1.347c813134",
|
|
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.
|
|
64
|
-
"@open-mercato/queue": "0.10.1-develop.
|
|
65
|
-
"@open-mercato/shared": "0.10.1-develop.
|
|
66
|
-
"@open-mercato/telemetry": "0.10.1-develop.
|
|
63
|
+
"@open-mercato/events": "0.10.1-develop.7804.1.347c813134",
|
|
64
|
+
"@open-mercato/queue": "0.10.1-develop.7804.1.347c813134",
|
|
65
|
+
"@open-mercato/shared": "0.10.1-develop.7804.1.347c813134",
|
|
66
|
+
"@open-mercato/telemetry": "0.10.1-develop.7804.1.347c813134",
|
|
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.
|
|
78
|
+
"@open-mercato/shared": "0.10.1-develop.7804.1.347c813134"
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
|
-
"@open-mercato/shared": "0.10.1-develop.
|
|
81
|
+
"@open-mercato/shared": "0.10.1-develop.7804.1.347c813134",
|
|
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
|
|
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 =
|
|
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)
|
package/src/lib/module-search.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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))
|