@clarify-labs/cli 0.11.0 → 0.11.1
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/output/cli/commands/mcp.d.ts +27 -0
- package/output/cli/mcp/orama-loader.d.ts +56 -0
- package/output/cli/mcp/search-server.d.ts +34 -0
- package/output/cli/program.d.ts +0 -1
- package/output/cli.cjs +294 -13
- package/output/cli.cjs.map +1 -1
- package/output/cli.js +295 -14
- package/output/cli.js.map +1 -1
- package/output/core/config/config-schema.d.ts +6 -220
- package/output/core/plugin/hooks.d.ts +0 -1
- package/output/core/runtime/html-template.d.ts +0 -6
- package/output/core/runtime/virtual-modules.d.ts +0 -4
- package/output/{engine-BfkoyVNo.cjs → engine-Cbn_coqg.cjs} +380 -28
- package/output/engine-Cbn_coqg.cjs.map +1 -0
- package/output/{engine-DfF5RJjC.js → engine-CxnKEKv8.js} +369 -29
- package/output/engine-CxnKEKv8.js.map +1 -0
- package/output/index.cjs +1 -1
- package/output/index.js +1 -1
- package/output/parsers/routes/routes.d.ts +1 -5
- package/output/plugins/content-artifacts/artifacts.d.ts +0 -2
- package/output/plugins/mcp-search/index.d.ts +15 -0
- package/output/plugins/mcp-search/mcp-config.d.ts +72 -0
- package/output/plugins/mcp-search/orama-index.d.ts +153 -0
- package/output/plugins/openapi/virtual-modules.d.ts +0 -1
- package/output/plugins/page-search/index.d.ts +2 -0
- package/output/types.d.ts +3 -2
- package/package.json +6 -4
- package/output/engine-BfkoyVNo.cjs.map +0 -1
- package/output/engine-DfF5RJjC.js.map +0 -1
- package/output/plugins/search-index/index.d.ts +0 -2
- /package/output/plugins/{search-index → page-search}/search-language.d.ts +0 -0
|
@@ -48,6 +48,8 @@ let node_fs = require("node:fs");
|
|
|
48
48
|
let vite = require("vite");
|
|
49
49
|
let zod = require("zod");
|
|
50
50
|
let yaml = require("yaml");
|
|
51
|
+
let _msgpack_msgpack = require("@msgpack/msgpack");
|
|
52
|
+
let _orama_orama = require("@orama/orama");
|
|
51
53
|
let node_url = require("node:url");
|
|
52
54
|
let _apidevtools_swagger_parser = require("@apidevtools/swagger-parser");
|
|
53
55
|
_apidevtools_swagger_parser = __toESM(_apidevtools_swagger_parser, 1);
|
|
@@ -999,7 +1001,7 @@ function featureSchema(shape, defaults) {
|
|
|
999
1001
|
} : value);
|
|
1000
1002
|
}
|
|
1001
1003
|
var clarifyFeaturesConfigSchema = zod.z.object({
|
|
1002
|
-
search: featureSchema({
|
|
1004
|
+
search: featureSchema({ mcp: zod.z.boolean().default(true) }, { mcp: true }),
|
|
1003
1005
|
repository: featureSchema(clarifyRepositoryConfigSchema.shape, {}),
|
|
1004
1006
|
themeEditor: featureSchema({}, {}),
|
|
1005
1007
|
openapi: featureSchema({ playground: zod.z.boolean().default(true) }, { playground: true })
|
|
@@ -1585,7 +1587,7 @@ function createViteAdapter(engine) {
|
|
|
1585
1587
|
}
|
|
1586
1588
|
var cliPackageVersion = {
|
|
1587
1589
|
name: "@clarify-labs/cli",
|
|
1588
|
-
version: "0.11.
|
|
1590
|
+
version: "0.11.1",
|
|
1589
1591
|
license: "AGPL-3.0-only",
|
|
1590
1592
|
type: "module",
|
|
1591
1593
|
repository: {
|
|
@@ -1626,6 +1628,9 @@ var cliPackageVersion = {
|
|
|
1626
1628
|
"@clarify-labs/templates": "workspace:*",
|
|
1627
1629
|
"@mdx-js/mdx": "^3.1.1",
|
|
1628
1630
|
"@mdx-js/rollup": "^3.1.1",
|
|
1631
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
1632
|
+
"@msgpack/msgpack": "^3.1.3",
|
|
1633
|
+
"@orama/orama": "^3.1.18",
|
|
1629
1634
|
"@shikijs/rehype": "^4.3.0",
|
|
1630
1635
|
"@tailwindcss/vite": "^4.3.2",
|
|
1631
1636
|
"@vitejs/plugin-react": "^6.0.3",
|
|
@@ -1644,7 +1649,6 @@ var cliPackageVersion = {
|
|
|
1644
1649
|
"rehype-raw": "^7.0.0",
|
|
1645
1650
|
"rehype-slug": "^6.0.0",
|
|
1646
1651
|
"remark": "^15.0.1",
|
|
1647
|
-
"remark-gfm": "^4.0.1",
|
|
1648
1652
|
"shiki": "4.3.0",
|
|
1649
1653
|
"unist-util-visit": "^5.1.0",
|
|
1650
1654
|
"vite": "^8.1.4",
|
|
@@ -1925,6 +1929,347 @@ function createHtmlShellPlugin() {
|
|
|
1925
1929
|
};
|
|
1926
1930
|
}
|
|
1927
1931
|
//#endregion
|
|
1932
|
+
//#region source/plugins/mcp-search/mcp-config.ts
|
|
1933
|
+
/**
|
|
1934
|
+
* Normalize a route prefix into a root-relative path segment.
|
|
1935
|
+
*
|
|
1936
|
+
* `'/'` -> `''`, `'/docs'` -> `'/docs'`, `'docs/'` -> `'/docs'`.
|
|
1937
|
+
*/
|
|
1938
|
+
function normalizePrefix(routePrefix) {
|
|
1939
|
+
if (!routePrefix || routePrefix === "/") return "";
|
|
1940
|
+
return `/${routePrefix.replace(/^\/+|\/+$/g, "")}`;
|
|
1941
|
+
}
|
|
1942
|
+
/**
|
|
1943
|
+
* Assemble the MCP site config from build-time metadata.
|
|
1944
|
+
*
|
|
1945
|
+
* Unlike the previous pagefind-based descriptor, this no longer reads an
|
|
1946
|
+
* entry JSON file - the Orama index is a single binary blob, so the caller
|
|
1947
|
+
* supplies the document count and locale list directly from the build step
|
|
1948
|
+
* that produced the index.
|
|
1949
|
+
*/
|
|
1950
|
+
function createMcpSiteConfig(projectConfig, options) {
|
|
1951
|
+
const prefix = normalizePrefix(projectConfig.routePrefix);
|
|
1952
|
+
return {
|
|
1953
|
+
version: 3,
|
|
1954
|
+
site: {
|
|
1955
|
+
title: projectConfig.title,
|
|
1956
|
+
description: projectConfig.description,
|
|
1957
|
+
url: projectConfig.siteUrl,
|
|
1958
|
+
routePrefix: projectConfig.routePrefix ?? "/"
|
|
1959
|
+
},
|
|
1960
|
+
capabilities: { search: {
|
|
1961
|
+
type: "search",
|
|
1962
|
+
indexPath: `${prefix}/mcp-search.msp`,
|
|
1963
|
+
defaultLocale: projectConfig.locales?.default,
|
|
1964
|
+
documentCount: options.documentCount,
|
|
1965
|
+
locales: options.locales
|
|
1966
|
+
} }
|
|
1967
|
+
};
|
|
1968
|
+
}
|
|
1969
|
+
//#endregion
|
|
1970
|
+
//#region source/plugins/mcp-search/orama-index.ts
|
|
1971
|
+
/**
|
|
1972
|
+
* Orama schema for the MCP search index.
|
|
1973
|
+
*
|
|
1974
|
+
* - `path`, `title`, `description`, `content` are full-text searchable strings.
|
|
1975
|
+
* - `locale` is an `enum` so we can use `where: { locale: { eq } }` filters at
|
|
1976
|
+
* query time. (`string` fields cannot be filtered in Orama.)
|
|
1977
|
+
* - `keywords` is a `string[]` also indexed for full-text matching.
|
|
1978
|
+
*/
|
|
1979
|
+
var mcpSearchSchema = {
|
|
1980
|
+
path: "string",
|
|
1981
|
+
title: "string",
|
|
1982
|
+
description: "string",
|
|
1983
|
+
locale: "enum",
|
|
1984
|
+
content: "string",
|
|
1985
|
+
keywords: "string[]"
|
|
1986
|
+
};
|
|
1987
|
+
/**
|
|
1988
|
+
* Intl.Segmenter cache keyed by BCP 47 tag. Segmenter construction is not
|
|
1989
|
+
* cheap, and the MCP server issues many queries against the same locale, so
|
|
1990
|
+
* we reuse instances. The cache is module-scoped: build and runtime share it
|
|
1991
|
+
* in the same process, and separate processes get their own - which is fine.
|
|
1992
|
+
*/
|
|
1993
|
+
var segmenterCache = /* @__PURE__ */ new Map();
|
|
1994
|
+
function getSegmenter(locale) {
|
|
1995
|
+
let seg = segmenterCache.get(locale);
|
|
1996
|
+
if (!seg) {
|
|
1997
|
+
seg = new Intl.Segmenter(locale, { granularity: "word" });
|
|
1998
|
+
segmenterCache.set(locale, seg);
|
|
1999
|
+
}
|
|
2000
|
+
return seg;
|
|
2001
|
+
}
|
|
2002
|
+
/**
|
|
2003
|
+
* Map a Clarify locale code (e.g. `zh-CN`, `en-US`, `ja-JP`) to a BCP 47 tag
|
|
2004
|
+
* suitable for `Intl.Segmenter`. Returns `null` for locales we intentionally
|
|
2005
|
+
* want to tokenize with the Latin/whitespace fallback (faster, and avoids
|
|
2006
|
+
* forcing CJK-style word segmentation on Latin scripts).
|
|
2007
|
+
*/
|
|
2008
|
+
function segmenterLocale(locale) {
|
|
2009
|
+
if (!locale) return null;
|
|
2010
|
+
const lower = locale.toLowerCase();
|
|
2011
|
+
if (lower.startsWith("zh")) return "zh-CN";
|
|
2012
|
+
if (lower.startsWith("ja")) return "ja-JP";
|
|
2013
|
+
if (lower.startsWith("ko")) return "ko-KR";
|
|
2014
|
+
return null;
|
|
2015
|
+
}
|
|
2016
|
+
/**
|
|
2017
|
+
* Tokenize text for Orama indexing and querying.
|
|
2018
|
+
*
|
|
2019
|
+
* CJK locales use `Intl.Segmenter` (word granularity), which splits
|
|
2020
|
+
* spaceless scripts into words and individual characters. Latin/whitespace
|
|
2021
|
+
* locales fall back to a Unicode-aware split on spaces and punctuation.
|
|
2022
|
+
*
|
|
2023
|
+
* All tokens are lowercased so queries match regardless of case.
|
|
2024
|
+
*/
|
|
2025
|
+
function tokenizeForSearch(text, locale) {
|
|
2026
|
+
if (!text) return [];
|
|
2027
|
+
const segLocale = segmenterLocale(locale);
|
|
2028
|
+
if (segLocale) {
|
|
2029
|
+
const seg = getSegmenter(segLocale);
|
|
2030
|
+
return Array.from(seg.segment(text)).filter((s) => s.isWordLike).map((s) => s.segment.toLowerCase());
|
|
2031
|
+
}
|
|
2032
|
+
return text.toLowerCase().split(/[\s\p{P}]+/u).filter(Boolean);
|
|
2033
|
+
}
|
|
2034
|
+
/**
|
|
2035
|
+
* Build an Orama tokenizer component bound to a specific default locale.
|
|
2036
|
+
*
|
|
2037
|
+
* The tokenizer receives the raw field value and the per-field language hint
|
|
2038
|
+
* from Orama (which we don't set per-field, so it falls back to the default
|
|
2039
|
+
* language). We resolve the segmenter locale from the default locale at
|
|
2040
|
+
* construction time so the same tokenizer handles all documents in a
|
|
2041
|
+
* predominantly single-locale index.
|
|
2042
|
+
*
|
|
2043
|
+
* For multi-locale indexes, the tokenizer is also used at query time. Since
|
|
2044
|
+
* the query locale is not known to Orama's tokenizer callback, we use the
|
|
2045
|
+
* index's default locale - this is acceptable because:
|
|
2046
|
+
* - CJK queries get CJK segmentation (correct)
|
|
2047
|
+
* - Latin queries against a CJK-default index still work because Latin
|
|
2048
|
+
* text has no word boundaries for CJK segmenters to mis-split; the
|
|
2049
|
+
* segmenter passes through Latin words as-is.
|
|
2050
|
+
*/
|
|
2051
|
+
function createMcpTokenizer(defaultLocale) {
|
|
2052
|
+
return {
|
|
2053
|
+
language: defaultLocale ?? "en",
|
|
2054
|
+
normalizationCache: /* @__PURE__ */ new Map(),
|
|
2055
|
+
tokenize(raw, language) {
|
|
2056
|
+
return tokenizeForSearch(typeof raw === "string" ? raw : String(raw ?? ""), language ?? defaultLocale);
|
|
2057
|
+
}
|
|
2058
|
+
};
|
|
2059
|
+
}
|
|
2060
|
+
/**
|
|
2061
|
+
* Compose the searchable text for a route. Shared between the Pagefind
|
|
2062
|
+
* (browser) and Orama (MCP) indexing paths so both backends see the same
|
|
2063
|
+
* field order and content.
|
|
2064
|
+
*/
|
|
2065
|
+
function routeSearchContent(route) {
|
|
2066
|
+
return [
|
|
2067
|
+
route.meta.title,
|
|
2068
|
+
route.meta.description,
|
|
2069
|
+
route.meta.keywords?.join(" "),
|
|
2070
|
+
route.meta.sections?.map((section) => section.title).join(" "),
|
|
2071
|
+
route.source.content
|
|
2072
|
+
].filter(Boolean).join("\n\n");
|
|
2073
|
+
}
|
|
2074
|
+
/**
|
|
2075
|
+
* Convert a `ContentRoute` into the document shape stored in the Orama index.
|
|
2076
|
+
*/
|
|
2077
|
+
function routeToSearchDocument(route, defaultLocale) {
|
|
2078
|
+
if (route.isBareAlias) return null;
|
|
2079
|
+
const content = routeSearchContent(route);
|
|
2080
|
+
if (!content.trim()) return null;
|
|
2081
|
+
return {
|
|
2082
|
+
path: route.path,
|
|
2083
|
+
title: route.meta.title ?? "",
|
|
2084
|
+
description: route.meta.description ?? "",
|
|
2085
|
+
locale: route.locale ?? defaultLocale ?? "en",
|
|
2086
|
+
content,
|
|
2087
|
+
keywords: route.meta.keywords ?? []
|
|
2088
|
+
};
|
|
2089
|
+
}
|
|
2090
|
+
/**
|
|
2091
|
+
* Create an Orama index pre-configured with the MCP schema and CJK-aware
|
|
2092
|
+
* tokenizer. Both the build step and the runtime loader use this so the
|
|
2093
|
+
* tokenizer is guaranteed to match between indexing and querying.
|
|
2094
|
+
*
|
|
2095
|
+
* Annotated as `AnyOrama` (Orama's exported base type) rather than inferred
|
|
2096
|
+
* via `ReturnType<typeof create>` because the inference would pull in
|
|
2097
|
+
* Orama's internal component types (`DocumentsStore`, `Index`, `Sorter`)
|
|
2098
|
+
* which are not exported and break `.d.ts` generation (TS2883).
|
|
2099
|
+
*/
|
|
2100
|
+
function createMcpSearchIndex(defaultLocale) {
|
|
2101
|
+
return (0, _orama_orama.create)({
|
|
2102
|
+
schema: mcpSearchSchema,
|
|
2103
|
+
components: { tokenizer: createMcpTokenizer(defaultLocale) }
|
|
2104
|
+
});
|
|
2105
|
+
}
|
|
2106
|
+
/**
|
|
2107
|
+
* Insert search documents into an Orama index.
|
|
2108
|
+
*/
|
|
2109
|
+
function indexSearchDocuments(db, documents) {
|
|
2110
|
+
if (documents.length === 0) return;
|
|
2111
|
+
(0, _orama_orama.insertMultiple)(db, documents);
|
|
2112
|
+
}
|
|
2113
|
+
/**
|
|
2114
|
+
* Build a ready-to-search Orama index from content routes.
|
|
2115
|
+
*
|
|
2116
|
+
* Filters out bare aliases and empty routes. Returns the populated db.
|
|
2117
|
+
*/
|
|
2118
|
+
function buildSearchIndex(routes, defaultLocale) {
|
|
2119
|
+
const db = createMcpSearchIndex(defaultLocale);
|
|
2120
|
+
const documents = [];
|
|
2121
|
+
for (const route of routes) {
|
|
2122
|
+
const doc = routeToSearchDocument(route, defaultLocale);
|
|
2123
|
+
if (doc) documents.push(doc);
|
|
2124
|
+
}
|
|
2125
|
+
indexSearchDocuments(db, documents);
|
|
2126
|
+
return {
|
|
2127
|
+
db,
|
|
2128
|
+
documentCount: documents.length
|
|
2129
|
+
};
|
|
2130
|
+
}
|
|
2131
|
+
/**
|
|
2132
|
+
* Collect the distinct locale codes that will be present in the Orama index,
|
|
2133
|
+
* in stable insertion order. Bare aliases are skipped (they duplicate the
|
|
2134
|
+
* default-locale route). Locales with no code fall back to the project
|
|
2135
|
+
* default so single-locale sites still surface one locale.
|
|
2136
|
+
*/
|
|
2137
|
+
function collectIndexedLocales(routes, defaultLocale) {
|
|
2138
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2139
|
+
const locales = [];
|
|
2140
|
+
for (const route of routes) {
|
|
2141
|
+
if (route.isBareAlias) continue;
|
|
2142
|
+
const code = route.locale ?? defaultLocale ?? "en";
|
|
2143
|
+
if (!seen.has(code)) {
|
|
2144
|
+
seen.add(code);
|
|
2145
|
+
locales.push(code);
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
return locales;
|
|
2149
|
+
}
|
|
2150
|
+
/**
|
|
2151
|
+
* Search an Orama MCP index.
|
|
2152
|
+
*
|
|
2153
|
+
* Applies an optional locale filter (via the `enum` field) and returns a
|
|
2154
|
+
* compact, LLM-friendly hit shape. Excerpts are derived from the matched
|
|
2155
|
+
* content: Orama does not return highlighted excerpts, so we synthesize a
|
|
2156
|
+
* short window around the first query term occurrence.
|
|
2157
|
+
*/
|
|
2158
|
+
function searchMcpIndex(db, params) {
|
|
2159
|
+
const limit = Math.max(1, Math.min(50, params.limit ?? 10));
|
|
2160
|
+
const result = (0, _orama_orama.search)(db, {
|
|
2161
|
+
term: params.query,
|
|
2162
|
+
limit,
|
|
2163
|
+
where: params.locale ? { locale: { eq: params.locale } } : void 0
|
|
2164
|
+
});
|
|
2165
|
+
return {
|
|
2166
|
+
hits: result.hits.map((hit) => {
|
|
2167
|
+
const doc = hit.document;
|
|
2168
|
+
return {
|
|
2169
|
+
path: doc.path,
|
|
2170
|
+
title: doc.title || guessTitleFromPath(doc.path),
|
|
2171
|
+
description: doc.description,
|
|
2172
|
+
locale: doc.locale,
|
|
2173
|
+
score: hit.score,
|
|
2174
|
+
excerpt: makeExcerpt(doc.content, params.query),
|
|
2175
|
+
keywords: doc.keywords
|
|
2176
|
+
};
|
|
2177
|
+
}),
|
|
2178
|
+
count: result.count
|
|
2179
|
+
};
|
|
2180
|
+
}
|
|
2181
|
+
/**
|
|
2182
|
+
* Build a short excerpt around the first occurrence of any query term in the
|
|
2183
|
+
* content. Falls back to the beginning of the content if no term is found.
|
|
2184
|
+
*/
|
|
2185
|
+
function makeExcerpt(content, query, maxLen = 240) {
|
|
2186
|
+
if (!content) return "";
|
|
2187
|
+
const terms = query.toLowerCase().split(/[\s\p{P}]+/u).filter(Boolean);
|
|
2188
|
+
const lower = content.toLowerCase();
|
|
2189
|
+
let pos = -1;
|
|
2190
|
+
for (const term of terms) {
|
|
2191
|
+
const i = lower.indexOf(term);
|
|
2192
|
+
if (i >= 0 && (pos < 0 || i < pos)) pos = i;
|
|
2193
|
+
}
|
|
2194
|
+
if (pos < 0) pos = 0;
|
|
2195
|
+
const half = Math.floor(maxLen / 2);
|
|
2196
|
+
const start = Math.max(0, pos - half);
|
|
2197
|
+
const end = Math.min(content.length, start + maxLen);
|
|
2198
|
+
const prefix = start > 0 ? "…" : "";
|
|
2199
|
+
const suffix = end < content.length ? "…" : "";
|
|
2200
|
+
return `${prefix}${content.slice(start, end).trim()}${suffix}`;
|
|
2201
|
+
}
|
|
2202
|
+
function guessTitleFromPath(path) {
|
|
2203
|
+
if (!path) return "(untitled)";
|
|
2204
|
+
const segments = path.split("/").filter(Boolean);
|
|
2205
|
+
if (segments.length === 0) return "Home";
|
|
2206
|
+
return segments[segments.length - 1].replace(/\.(md|mdx|html)$/, "").replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
2207
|
+
}
|
|
2208
|
+
/**
|
|
2209
|
+
* Serialize an Orama index to a msgpack binary buffer.
|
|
2210
|
+
*
|
|
2211
|
+
* Uses `save()` to extract the raw index data, then msgpack-encodes it for a
|
|
2212
|
+
* compact binary representation. The same schema + tokenizer must be supplied
|
|
2213
|
+
* when loading so query-time tokenization matches indexing-time tokenization.
|
|
2214
|
+
*/
|
|
2215
|
+
function serializeSearchIndex(db) {
|
|
2216
|
+
return (0, _msgpack_msgpack.encode)((0, _orama_orama.save)(db));
|
|
2217
|
+
}
|
|
2218
|
+
/**
|
|
2219
|
+
* Deserialize a msgpack binary buffer into a ready-to-search Orama index.
|
|
2220
|
+
*
|
|
2221
|
+
* The caller must supply the same `defaultLocale` used when the index was
|
|
2222
|
+
* built, so the tokenizer matches. A mismatch would not corrupt data but
|
|
2223
|
+
* would cause CJK query terms to be tokenized incorrectly (e.g. "文档" left
|
|
2224
|
+
* as a single token instead of split into "文" + "档").
|
|
2225
|
+
*/
|
|
2226
|
+
function deserializeSearchIndex(buffer, defaultLocale) {
|
|
2227
|
+
const raw = (0, _msgpack_msgpack.decode)(buffer);
|
|
2228
|
+
const db = createMcpSearchIndex(defaultLocale);
|
|
2229
|
+
(0, _orama_orama.load)(db, raw);
|
|
2230
|
+
return db;
|
|
2231
|
+
}
|
|
2232
|
+
//#endregion
|
|
2233
|
+
//#region source/plugins/mcp-search/index.ts
|
|
2234
|
+
/**
|
|
2235
|
+
* MCP search index plugin.
|
|
2236
|
+
*
|
|
2237
|
+
* Generates the Orama binary index (`mcp-search.msp`) and the MCP site
|
|
2238
|
+
* descriptor (`mcp.json`) as build assets consumed by the `clarify mcp`
|
|
2239
|
+
* command to serve documentation search to MCP-aware clients (e.g. AI coding
|
|
2240
|
+
* assistants).
|
|
2241
|
+
*
|
|
2242
|
+
* This plugin is independent of `clarify:page-search` (Pagefind), which
|
|
2243
|
+
* powers in-browser search. The MCP server runs in Node and needs a
|
|
2244
|
+
* self-contained, compact index with CJK-aware tokenization that doesn't
|
|
2245
|
+
* depend on the browser-only Pagefind runtime.
|
|
2246
|
+
*/
|
|
2247
|
+
function createMcpSearchPlugin() {
|
|
2248
|
+
return {
|
|
2249
|
+
name: "clarify:mcp-search",
|
|
2250
|
+
hooks: { "build:assets"(ctx) {
|
|
2251
|
+
const searchConfig = ctx.projectConfig.features.search;
|
|
2252
|
+
if (!searchConfig.enabled || !searchConfig.mcp) return [];
|
|
2253
|
+
const defaultLocale = ctx.projectConfig.locales?.default;
|
|
2254
|
+
const { db, documentCount } = buildSearchIndex(ctx.routes, defaultLocale);
|
|
2255
|
+
const locales = collectIndexedLocales(ctx.routes, defaultLocale);
|
|
2256
|
+
const mcpConfig = createMcpSiteConfig(ctx.projectConfig, {
|
|
2257
|
+
documentCount,
|
|
2258
|
+
locales
|
|
2259
|
+
});
|
|
2260
|
+
const assets = [{
|
|
2261
|
+
fileName: `${mcpConfig.capabilities.search?.indexPath.replace(/\/mcp-search\.msp$/, "") ?? ""}/mcp-search.msp`.replace(/^\//, ""),
|
|
2262
|
+
source: serializeSearchIndex(db)
|
|
2263
|
+
}, {
|
|
2264
|
+
fileName: "mcp.json",
|
|
2265
|
+
source: `${JSON.stringify(mcpConfig, null, 2)}\n`
|
|
2266
|
+
}];
|
|
2267
|
+
console.log(`[clarify] MCP search index (${documentCount} docs, ${locales.length} locales) written to mcp-search.msp.`);
|
|
2268
|
+
return assets;
|
|
2269
|
+
} }
|
|
2270
|
+
};
|
|
2271
|
+
}
|
|
2272
|
+
//#endregion
|
|
1928
2273
|
//#region source/plugins/openapi/parser.ts
|
|
1929
2274
|
var OPENAPI_HTTP_METHODS = [
|
|
1930
2275
|
"get",
|
|
@@ -2347,12 +2692,12 @@ function removeClarifyTempDir(path) {
|
|
|
2347
2692
|
});
|
|
2348
2693
|
}
|
|
2349
2694
|
//#endregion
|
|
2350
|
-
//#region source/plugins/search
|
|
2695
|
+
//#region source/plugins/page-search/search-language.ts
|
|
2351
2696
|
function toPagefindLanguage(locale) {
|
|
2352
2697
|
return locale?.trim().replace(/_/g, "-").split("-").filter(Boolean).join("-").toLowerCase() || "en";
|
|
2353
2698
|
}
|
|
2354
2699
|
//#endregion
|
|
2355
|
-
//#region source/plugins/search
|
|
2700
|
+
//#region source/plugins/page-search/index.ts
|
|
2356
2701
|
var pagefindContentTypes = {
|
|
2357
2702
|
".css": "text/css; charset=utf-8",
|
|
2358
2703
|
".js": "text/javascript; charset=utf-8",
|
|
@@ -2392,15 +2737,6 @@ async function generatePagefindIndex(options, pagefind) {
|
|
|
2392
2737
|
if (!pagefind) await runtime.close();
|
|
2393
2738
|
}
|
|
2394
2739
|
}
|
|
2395
|
-
function routeSearchContent(route) {
|
|
2396
|
-
return [
|
|
2397
|
-
route.meta.title,
|
|
2398
|
-
route.meta.description,
|
|
2399
|
-
route.meta.keywords?.join(" "),
|
|
2400
|
-
route.meta.sections?.map((section) => section.title).join(" "),
|
|
2401
|
-
route.source.content
|
|
2402
|
-
].filter(Boolean).join("\n\n");
|
|
2403
|
-
}
|
|
2404
2740
|
async function generateDevSearchIndex(ctx, root, pagefind) {
|
|
2405
2741
|
const { errors, index } = await pagefind.createIndex({
|
|
2406
2742
|
includeCharacters: "._-/#:@",
|
|
@@ -2457,9 +2793,9 @@ function serveDevPagefind(server, getRoot, ctx) {
|
|
|
2457
2793
|
(0, node_fs.createReadStream)(filePath).pipe(res);
|
|
2458
2794
|
});
|
|
2459
2795
|
}
|
|
2460
|
-
function
|
|
2796
|
+
function createPageSearchPlugin() {
|
|
2461
2797
|
return {
|
|
2462
|
-
name: "clarify:search
|
|
2798
|
+
name: "clarify:page-search",
|
|
2463
2799
|
hooks: {
|
|
2464
2800
|
async "dev:configureServer"(server, ctx) {
|
|
2465
2801
|
if (!ctx.projectConfig.features.search.enabled) return;
|
|
@@ -2506,7 +2842,7 @@ function createSearchIndexPlugin() {
|
|
|
2506
2842
|
const outputDirectory = ctx.generateOptions.outputDirectory;
|
|
2507
2843
|
if (!outputDirectory) return;
|
|
2508
2844
|
const pageCount = await generatePagefindIndex({ outputRoot: (0, node_path.resolve)(ctx.generateOptions.projectRoot, outputDirectory) });
|
|
2509
|
-
console.log(`[clarify]
|
|
2845
|
+
console.log(`[clarify] Page search index generated for ${pageCount} pages.`);
|
|
2510
2846
|
}
|
|
2511
2847
|
}
|
|
2512
2848
|
};
|
|
@@ -2655,7 +2991,8 @@ function createBuiltinPlugins(options = {}) {
|
|
|
2655
2991
|
createSourceLinksPlugin(),
|
|
2656
2992
|
createContentArtifactsPlugin(),
|
|
2657
2993
|
createSeoPlugin(),
|
|
2658
|
-
|
|
2994
|
+
createPageSearchPlugin(),
|
|
2995
|
+
createMcpSearchPlugin()
|
|
2659
2996
|
];
|
|
2660
2997
|
if (options.htmlShell ?? true) plugins.push(createHtmlShellPlugin());
|
|
2661
2998
|
return plugins;
|
|
@@ -3002,16 +3339,19 @@ async function renderSSGRoutes(routes, contextOrConfig, outputDirectory, ssrBund
|
|
|
3002
3339
|
const { render } = await import((0, node_url.pathToFileURL)(ssrBundlePath).href);
|
|
3003
3340
|
const template = readIndexHtml(outputDirectory);
|
|
3004
3341
|
if (!template) throw new Error(`[clarify] index.html not found in outputDirectory "${outputDirectory}". Make sure Vite build produces it.`);
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
(
|
|
3009
|
-
(
|
|
3342
|
+
await Promise.all(routes.map(async (route) => {
|
|
3343
|
+
try {
|
|
3344
|
+
const appHtml = await render(route.path);
|
|
3345
|
+
const finalHtml = injectSSRIntoTemplate(template, appHtml, contextOrConfig, route);
|
|
3346
|
+
for (const outFile of routeOutputFiles(outputDirectory, route)) {
|
|
3347
|
+
(0, node_fs.mkdirSync)((0, node_path.dirname)(outFile), { recursive: true });
|
|
3348
|
+
(0, node_fs.writeFileSync)(outFile, finalHtml, "utf-8");
|
|
3349
|
+
}
|
|
3350
|
+
} catch (err) {
|
|
3351
|
+
console.error(`[clarify] Failed to render route "${route.path}":`, err);
|
|
3352
|
+
throw err;
|
|
3010
3353
|
}
|
|
3011
|
-
}
|
|
3012
|
-
console.error(`[clarify] Failed to render route "${route.path}":`, err);
|
|
3013
|
-
throw err;
|
|
3014
|
-
}
|
|
3354
|
+
}));
|
|
3015
3355
|
}
|
|
3016
3356
|
//#endregion
|
|
3017
3357
|
//#region source/core/runtime/startup.ts
|
|
@@ -3498,5 +3838,17 @@ Object.defineProperty(exports, "defineConfig", {
|
|
|
3498
3838
|
return defineConfig;
|
|
3499
3839
|
}
|
|
3500
3840
|
});
|
|
3841
|
+
Object.defineProperty(exports, "deserializeSearchIndex", {
|
|
3842
|
+
enumerable: true,
|
|
3843
|
+
get: function() {
|
|
3844
|
+
return deserializeSearchIndex;
|
|
3845
|
+
}
|
|
3846
|
+
});
|
|
3847
|
+
Object.defineProperty(exports, "searchMcpIndex", {
|
|
3848
|
+
enumerable: true,
|
|
3849
|
+
get: function() {
|
|
3850
|
+
return searchMcpIndex;
|
|
3851
|
+
}
|
|
3852
|
+
});
|
|
3501
3853
|
|
|
3502
|
-
//# sourceMappingURL=engine-
|
|
3854
|
+
//# sourceMappingURL=engine-Cbn_coqg.cjs.map
|