@clarify-labs/cli 0.11.0 → 0.11.2

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