@clarify-labs/cli 0.6.4 → 0.6.6

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.
@@ -40,6 +40,7 @@ let gray_matter = require("gray-matter");
40
40
  gray_matter = __toESM(gray_matter, 1);
41
41
  let _apidevtools_swagger_parser = require("@apidevtools/swagger-parser");
42
42
  _apidevtools_swagger_parser = __toESM(_apidevtools_swagger_parser, 1);
43
+ let node_fs_promises = require("node:fs/promises");
43
44
  let zod = require("zod");
44
45
  let node_os = require("node:os");
45
46
  let node_url = require("node:url");
@@ -76,6 +77,11 @@ function rehypeParseCodeBlocks() {
76
77
  const language = getLanguage(node.properties.className);
77
78
  parentNode.properties.language = language;
78
79
  node.properties.language = language;
80
+ for (const key of [
81
+ "title",
82
+ "tag",
83
+ "label"
84
+ ]) if (node.properties[key] !== void 0) parentNode.properties[key] = node.properties[key];
79
85
  });
80
86
  };
81
87
  }
@@ -129,6 +135,191 @@ var rehypePlugins = [
129
135
  rehypeShiki
130
136
  ];
131
137
  //#endregion
138
+ //#region source/plugins/content-artifacts/artifacts.ts
139
+ var UTF8_SIGNATURE = "";
140
+ function normalizeBasePath(basePath) {
141
+ if (!basePath || basePath === "/") return "";
142
+ return "/" + basePath.replace(/^\/+|\/+$/g, "");
143
+ }
144
+ function containsNonAscii(content) {
145
+ return [...content].some((character) => character.codePointAt(0) > 127);
146
+ }
147
+ function withUtf8Signature(content) {
148
+ if (!containsNonAscii(content) || content.startsWith(UTF8_SIGNATURE)) return content;
149
+ return `${UTF8_SIGNATURE}${content}`;
150
+ }
151
+ function shouldUseUtf8Signature(route) {
152
+ if (route.kind === "mdx") return true;
153
+ return /\.ya?ml$/i.test(route.contentArtifactUrl ?? "");
154
+ }
155
+ function routeToMarkdownArtifactUrl(routePath) {
156
+ return `${routePath === "/" ? "/index" : routePath.replace(/\/$/, "")}.md`;
157
+ }
158
+ function routeToOpenAPIArtifactUrl(routePath, filePath) {
159
+ const extension = (0, node_path.extname)(filePath).toLowerCase() || ".json";
160
+ return `${routePath === "/" ? "/index" : routePath.replace(/\/$/, "")}.openapi${extension}`;
161
+ }
162
+ function attachContentArtifactUrls(routes) {
163
+ for (const route of routes) route.contentArtifactUrl = route.kind === "openapi" ? routeToOpenAPIArtifactUrl(route.path, route.filePath) : routeToMarkdownArtifactUrl(route.path);
164
+ }
165
+ function readRouteContent(route) {
166
+ if (route.content !== void 0) return route.content;
167
+ throw new Error(`Route content is missing from route context: ${route.filePath}`);
168
+ }
169
+ function readRouteArtifactContent(route) {
170
+ const content = readRouteContent(route);
171
+ return shouldUseUtf8Signature(route) ? withUtf8Signature(content) : content;
172
+ }
173
+ function writeContentArtifactFiles(routes, outputDirectory) {
174
+ for (const route of routes) {
175
+ if (!route.contentArtifactUrl) continue;
176
+ const outFile = (0, node_path.join)(outputDirectory, route.contentArtifactUrl.replace(/^\//, ""));
177
+ (0, node_fs.mkdirSync)((0, node_path.dirname)(outFile), { recursive: true });
178
+ (0, node_fs.writeFileSync)(outFile, readRouteArtifactContent(route), "utf-8");
179
+ }
180
+ }
181
+ function createLlmsTxt(routes, projectConfig) {
182
+ const basePath = normalizeBasePath(projectConfig.routePrefix);
183
+ const lines = [`# ${projectConfig.title}`, ""];
184
+ if (projectConfig.description) lines.push(`> ${projectConfig.description}`, "");
185
+ lines.push("## Docs");
186
+ for (const route of routes.filter((route) => route.kind === "mdx")) {
187
+ if (!route.contentArtifactUrl) continue;
188
+ lines.push(`- [${route.title}](${basePath}${route.contentArtifactUrl})`);
189
+ }
190
+ const openApiRoutes = routes.filter((route) => route.kind === "openapi");
191
+ if (openApiRoutes.length > 0) {
192
+ lines.push("", "## OpenAPI");
193
+ for (const route of openApiRoutes) {
194
+ if (!route.contentArtifactUrl) continue;
195
+ lines.push(`- [${route.title}](${basePath}${route.contentArtifactUrl})`);
196
+ }
197
+ }
198
+ return `${lines.join("\n")}\n`;
199
+ }
200
+ function createLlmsTxtArtifact(routes, projectConfig) {
201
+ return withUtf8Signature(createLlmsTxt(routes, projectConfig));
202
+ }
203
+ function writeLlmsTxt(routes, projectConfig, outputDirectory) {
204
+ (0, node_fs.mkdirSync)(outputDirectory, { recursive: true });
205
+ (0, node_fs.writeFileSync)((0, node_path.join)(outputDirectory, "llms.txt"), createLlmsTxtArtifact(routes, projectConfig), "utf-8");
206
+ }
207
+ //#endregion
208
+ //#region source/plugins/content-artifacts/server.ts
209
+ function normalizeRoutePrefix$1(routePrefix) {
210
+ if (!routePrefix || routePrefix === "/") return "";
211
+ return `/${routePrefix.replace(/^\/+|\/+$/g, "")}`;
212
+ }
213
+ function resolveContentArtifactPath(url, projectConfig) {
214
+ const requestPath = url?.split("?")[0] ?? "";
215
+ const basePath = normalizeRoutePrefix$1(projectConfig.routePrefix);
216
+ return basePath && requestPath.startsWith(basePath) ? requestPath.slice(basePath.length) || "/" : requestPath;
217
+ }
218
+ function resolveContentArtifactType(route) {
219
+ if (route.kind === "openapi" && /\.ya?ml$/i.test(route.contentArtifactUrl ?? "")) return "text/yaml; charset=utf-8";
220
+ return route.kind === "openapi" ? "application/json; charset=utf-8" : "text/markdown; charset=utf-8";
221
+ }
222
+ function serveContentArtifacts(req, res, projectConfig, routes) {
223
+ const contentPath = resolveContentArtifactPath(req.url, projectConfig);
224
+ if (contentPath === "/llms.txt") {
225
+ res.statusCode = 200;
226
+ res.setHeader("Content-Type", "text/plain; charset=utf-8");
227
+ res.end(createLlmsTxtArtifact(routes, projectConfig), "utf8");
228
+ return true;
229
+ }
230
+ const route = routes.find((route) => route.contentArtifactUrl === contentPath);
231
+ if (!route) return false;
232
+ res.statusCode = 200;
233
+ res.setHeader("Content-Type", resolveContentArtifactType(route));
234
+ res.end(readRouteArtifactContent(route), "utf8");
235
+ return true;
236
+ }
237
+ //#endregion
238
+ //#region source/plugins/content-artifacts/index.ts
239
+ function createContentArtifactsPlugin() {
240
+ return {
241
+ name: "clarify:content-artifacts",
242
+ hooks: {
243
+ "routes:resolved": (input) => {
244
+ attachContentArtifactUrls(input.routes);
245
+ return input;
246
+ },
247
+ "dev:configureServer": (server, ctx) => {
248
+ server.middlewares.use((req, res, next) => {
249
+ if (serveContentArtifacts(req, res, ctx.projectConfig, ctx.routes)) return;
250
+ next();
251
+ });
252
+ },
253
+ "build:done": (ctx) => {
254
+ const outputDirectory = ctx.generateOptions.outputDirectory;
255
+ if (!outputDirectory) return;
256
+ const outputRoot = (0, node_path.join)(ctx.generateOptions.projectRoot, outputDirectory);
257
+ writeContentArtifactFiles(ctx.routes, outputRoot);
258
+ writeLlmsTxt(ctx.routes, ctx.projectConfig, outputRoot);
259
+ }
260
+ }
261
+ };
262
+ }
263
+ //#endregion
264
+ //#region source/plugins/html-shell/index.ts
265
+ var THEME_BOOTSTRAP_SCRIPT = `(function(){try{var e=localStorage.getItem('clarify:theme');var t=e==='dark'||e==='light'||e==='system'?e:'system';var r=t==='system'?window.matchMedia('(prefers-color-scheme: dark)').matches:t==='dark';document.documentElement.classList.toggle('dark',r);document.documentElement.style.colorScheme=r?'dark':'light'}catch(e){}})();`;
266
+ var REL_ICON_RE = /<link\s+[^>]*rel=["'][^"']*\bicon\b[^"']*["'][^>]*>/i;
267
+ function inferIconType(href) {
268
+ const path = href.split(/[?#]/, 1)[0]?.toLowerCase() ?? href.toLowerCase();
269
+ if (path.endsWith(".svg")) return "image/svg+xml";
270
+ if (path.endsWith(".png")) return "image/png";
271
+ if (path.endsWith(".ico")) return "image/x-icon";
272
+ if (path.endsWith(".webp")) return "image/webp";
273
+ }
274
+ function createIconTag(href, media) {
275
+ return {
276
+ tag: "link",
277
+ attrs: {
278
+ rel: "icon",
279
+ ...inferIconType(href) ? { type: inferIconType(href) } : {},
280
+ href,
281
+ ...media ? { media } : {}
282
+ },
283
+ injectTo: "head"
284
+ };
285
+ }
286
+ function createIconTags(icon) {
287
+ if (!icon) return [];
288
+ if (typeof icon === "string") return [createIconTag(icon)];
289
+ const lightIcon = icon.light ?? icon.dark;
290
+ const darkIcon = icon.dark ?? icon.light;
291
+ if (!lightIcon || !darkIcon) return [];
292
+ if (lightIcon === darkIcon) return [createIconTag(lightIcon)];
293
+ return [createIconTag(lightIcon, "(prefers-color-scheme: light)"), createIconTag(darkIcon, "(prefers-color-scheme: dark)")];
294
+ }
295
+ function createHtmlShellPlugin() {
296
+ return {
297
+ name: "clarify:html-shell",
298
+ hooks: { "html:transform": (input, ctx) => ({
299
+ html: input.html,
300
+ tags: [
301
+ ...input.tags,
302
+ ...REL_ICON_RE.test(input.html) ? [] : createIconTags(ctx.projectConfig.favicon ?? ctx.projectConfig.logo),
303
+ {
304
+ tag: "script",
305
+ children: THEME_BOOTSTRAP_SCRIPT,
306
+ injectTo: "head-prepend"
307
+ },
308
+ {
309
+ tag: "script",
310
+ attrs: {
311
+ type: "module",
312
+ src: input.clientEntryId
313
+ },
314
+ injectTo: "body"
315
+ }
316
+ ],
317
+ clientEntryId: input.clientEntryId,
318
+ dev: input.dev
319
+ }) }
320
+ };
321
+ }
322
+ //#endregion
132
323
  //#region source/parsers/frontmatter.ts
133
324
  /**
134
325
  * 在内容入口统一解析 MDX frontmatter,并返回可供后续流程复用的标准化正文。
@@ -184,10 +375,21 @@ function normalizePath(path) {
184
375
  const clean = "/" + path.replace(/^\/+|\/+$/g, "");
185
376
  return clean === "/" ? "/" : clean.replace(/\/+/g, "/");
186
377
  }
378
+ function pathSegmentFromOpenAPITag(tag) {
379
+ return tag.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "tag";
380
+ }
381
+ function openAPITagsPathSegment(tags) {
382
+ return tags.map(pathSegmentFromOpenAPITag).join("-");
383
+ }
187
384
  function basePathFromRef(ref) {
188
385
  const withoutExtension = ref.replace(/\.mdx?$/, "").replace(/\.openapi\.(json|yaml|yml)$/, "");
189
386
  return normalizePath(withoutExtension === "index" ? "/" : withoutExtension.replace(/\/index$/, ""));
190
387
  }
388
+ function openAPIPagePathFromRef(ref, tagFilter) {
389
+ const basePath = basePathFromRef(ref);
390
+ if (!tagFilter?.length) return basePath;
391
+ return normalizePath(`${basePath}/${openAPITagsPathSegment(tagFilter)}`);
392
+ }
191
393
  function routePathFromRef(ref) {
192
394
  return basePathFromRef(ref);
193
395
  }
@@ -246,7 +448,7 @@ function frontmatterKeywords(frontmatter) {
246
448
  function buildNavigation(routes) {
247
449
  const root = [];
248
450
  for (const route of routes) {
249
- if (route.path === "/") continue;
451
+ if (route.path === "/" || (route.basePath ?? route.path) === "/404") continue;
250
452
  const parts = route.path.replace(/^\//, "").split("/");
251
453
  let current = root;
252
454
  for (let i = 0; i < parts.length; i++) {
@@ -273,6 +475,7 @@ function resolvePageItem(item) {
273
475
  if (typeof item === "string") return { pageRef: item };
274
476
  if ("openapi" in item) return {
275
477
  openapiRef: item.openapi,
478
+ openapiTagFilter: item.filter?.tags,
276
479
  title: item.title,
277
480
  icon: item.icon
278
481
  };
@@ -311,9 +514,9 @@ function buildNavigationFromConfig(routes, config) {
311
514
  const routeMap = new Map(routes.map((r) => [r.path, r]));
312
515
  return config.map((group) => {
313
516
  const children = group.pages.map((item) => {
314
- const { pageRef, openapiRef, redirect, title, icon } = resolvePageItem(item);
517
+ const { pageRef, openapiRef, openapiTagFilter, redirect, title, icon } = resolvePageItem(item);
315
518
  if (openapiRef) {
316
- const path = "/" + openapiRef.replace(/\.openapi\.(json|yaml|yml)$/, "");
519
+ const path = openAPIPagePathFromRef(openapiRef, openapiTagFilter);
317
520
  const route = routeMap.get(path);
318
521
  return {
319
522
  path,
@@ -355,227 +558,43 @@ function buildLocalizedNavigationForLocale(routes, config, locale, i18n) {
355
558
  const routeMap = new Map(routes.map((route) => [route.basePath ?? route.path, route]));
356
559
  return config.map((group) => {
357
560
  const children = group.pages.map((item) => {
358
- const { pageRef, openapiRef, redirect, title, icon } = resolvePageItem(item);
561
+ const { pageRef, openapiRef, openapiTagFilter, redirect, title, icon } = resolvePageItem(item);
359
562
  const ref = openapiRef ?? pageRef ?? "";
360
- const basePath = basePathFromRef(ref);
563
+ const basePath = openapiRef ? openAPIPagePathFromRef(openapiRef, openapiTagFilter) : basePathFromRef(ref);
361
564
  const route = routeMap.get(basePath);
362
565
  const path = route?.path ?? localizedRoutePath(basePath, locale, i18n);
363
566
  return {
364
567
  path: (redirect ? localizedRoutePath(basePathFromRef(redirect), locale, i18n) : void 0) ?? path,
365
568
  title: resolveLocalizedText(title, locale, i18n.defaultLocale) ?? route?.title ?? kebabToTitle$1(basePath.split("/").pop() ?? ref),
366
569
  icon,
367
- sections: route?.sections ? navigationSections(route.sections) : void 0
368
- };
369
- });
370
- return {
371
- path: children[0]?.path ?? localizedRoutePath("/", locale, i18n),
372
- title: resolveLocalizedText(group.group, locale, i18n.defaultLocale) ?? "",
373
- icon: group.icon,
374
- children
375
- };
376
- });
377
- }
378
- function buildLocalizedNavigationFromTabsConfig(routes, tabs, i18n) {
379
- if (!i18n) return void 0;
380
- const result = {};
381
- for (const locale of i18n.locales) {
382
- const localeRoutes = routes.filter((route) => route.locale === locale.code);
383
- result[locale.code] = { tabs: tabs.map((tab) => {
384
- const children = buildLocalizedNavigationForLocale(localeRoutes, tab.pages, locale.code, i18n);
385
- return {
386
- type: "tab",
387
- path: firstNavigationPath(children),
388
- title: resolveLocalizedText(tab.tab, locale.code, i18n.defaultLocale) ?? "",
389
- icon: tab.icon,
390
- children
391
- };
392
- }) };
393
- }
394
- return result;
395
- }
396
- //#endregion
397
- //#region source/plugins/content-artifacts/artifacts.ts
398
- var UTF8_SIGNATURE = "";
399
- function normalizeBasePath(basePath) {
400
- if (!basePath || basePath === "/") return "";
401
- return "/" + basePath.replace(/^\/+|\/+$/g, "");
402
- }
403
- function containsNonAscii(content) {
404
- return [...content].some((character) => character.codePointAt(0) > 127);
405
- }
406
- function withUtf8Signature(content) {
407
- if (!containsNonAscii(content) || content.startsWith(UTF8_SIGNATURE)) return content;
408
- return `${UTF8_SIGNATURE}${content}`;
409
- }
410
- function shouldUseUtf8Signature(route) {
411
- if (route.kind === "mdx") return true;
412
- return /\.ya?ml$/i.test(route.contentArtifactUrl ?? "");
413
- }
414
- function routeToMarkdownArtifactUrl(routePath) {
415
- return `${routePath === "/" ? "/index" : routePath.replace(/\/$/, "")}.md`;
416
- }
417
- function routeToOpenAPIArtifactUrl(routePath, filePath) {
418
- const extension = (0, node_path.extname)(filePath).toLowerCase() || ".json";
419
- return `${routePath === "/" ? "/index" : routePath.replace(/\/$/, "")}.openapi${extension}`;
420
- }
421
- function attachContentArtifactUrls(routes) {
422
- for (const route of routes) route.contentArtifactUrl = route.kind === "openapi" ? routeToOpenAPIArtifactUrl(route.path, route.filePath) : routeToMarkdownArtifactUrl(route.path);
423
- }
424
- function readRouteContent(route) {
425
- if (route.content !== void 0) return route.content;
426
- throw new Error(`Route content is missing from route context: ${route.filePath}`);
427
- }
428
- function readRouteArtifactContent(route) {
429
- const content = readRouteContent(route);
430
- return shouldUseUtf8Signature(route) ? withUtf8Signature(content) : content;
431
- }
432
- function writeContentArtifactFiles(routes, outputDirectory) {
433
- for (const route of routes) {
434
- if (!route.contentArtifactUrl) continue;
435
- const outFile = (0, node_path.join)(outputDirectory, route.contentArtifactUrl.replace(/^\//, ""));
436
- (0, node_fs.mkdirSync)((0, node_path.dirname)(outFile), { recursive: true });
437
- (0, node_fs.writeFileSync)(outFile, readRouteArtifactContent(route), "utf-8");
438
- }
439
- }
440
- function createLlmsTxt(routes, projectConfig) {
441
- const basePath = normalizeBasePath(projectConfig.routePrefix);
442
- const lines = [`# ${projectConfig.title}`, ""];
443
- if (projectConfig.description) lines.push(`> ${projectConfig.description}`, "");
444
- lines.push("## Docs");
445
- for (const route of routes.filter((route) => route.kind === "mdx")) {
446
- if (!route.contentArtifactUrl) continue;
447
- lines.push(`- [${route.title}](${basePath}${route.contentArtifactUrl})`);
448
- }
449
- const openApiRoutes = routes.filter((route) => route.kind === "openapi");
450
- if (openApiRoutes.length > 0) {
451
- lines.push("", "## OpenAPI");
452
- for (const route of openApiRoutes) {
453
- if (!route.contentArtifactUrl) continue;
454
- lines.push(`- [${route.title}](${basePath}${route.contentArtifactUrl})`);
455
- }
456
- }
457
- return `${lines.join("\n")}\n`;
458
- }
459
- function createLlmsTxtArtifact(routes, projectConfig) {
460
- return withUtf8Signature(createLlmsTxt(routes, projectConfig));
461
- }
462
- function writeLlmsTxt(routes, projectConfig, outputDirectory) {
463
- (0, node_fs.mkdirSync)(outputDirectory, { recursive: true });
464
- (0, node_fs.writeFileSync)((0, node_path.join)(outputDirectory, "llms.txt"), createLlmsTxtArtifact(routes, projectConfig), "utf-8");
465
- }
466
- //#endregion
467
- //#region source/plugins/content-artifacts/server.ts
468
- function normalizeRoutePrefix(routePrefix) {
469
- if (!routePrefix || routePrefix === "/") return "";
470
- return `/${routePrefix.replace(/^\/+|\/+$/g, "")}`;
471
- }
472
- function resolveContentArtifactPath(url, projectConfig) {
473
- const requestPath = url?.split("?")[0] ?? "";
474
- const basePath = normalizeRoutePrefix(projectConfig.routePrefix);
475
- return basePath && requestPath.startsWith(basePath) ? requestPath.slice(basePath.length) || "/" : requestPath;
476
- }
477
- function resolveContentArtifactType(route) {
478
- if (route.kind === "openapi" && /\.ya?ml$/i.test(route.contentArtifactUrl ?? "")) return "text/yaml; charset=utf-8";
479
- return route.kind === "openapi" ? "application/json; charset=utf-8" : "text/markdown; charset=utf-8";
480
- }
481
- function serveContentArtifacts(req, res, projectConfig, routes) {
482
- const contentPath = resolveContentArtifactPath(req.url, projectConfig);
483
- if (contentPath === "/llms.txt") {
484
- res.statusCode = 200;
485
- res.setHeader("Content-Type", "text/plain; charset=utf-8");
486
- res.end(createLlmsTxtArtifact(routes, projectConfig), "utf8");
487
- return true;
488
- }
489
- const route = routes.find((route) => route.contentArtifactUrl === contentPath);
490
- if (!route) return false;
491
- res.statusCode = 200;
492
- res.setHeader("Content-Type", resolveContentArtifactType(route));
493
- res.end(readRouteArtifactContent(route), "utf8");
494
- return true;
495
- }
496
- //#endregion
497
- //#region source/plugins/content-artifacts/index.ts
498
- function createContentArtifactsPlugin() {
499
- return {
500
- name: "clarify:content-artifacts",
501
- hooks: {
502
- "routes:resolved": (input) => {
503
- attachContentArtifactUrls(input.routes);
504
- return input;
505
- },
506
- "dev:configureServer": (server, ctx) => {
507
- server.middlewares.use((req, res, next) => {
508
- if (serveContentArtifacts(req, res, ctx.projectConfig, ctx.routes)) return;
509
- next();
510
- });
511
- },
512
- "build:done": (ctx) => {
513
- const outputDirectory = ctx.generateOptions.outputDirectory;
514
- if (!outputDirectory) return;
515
- writeContentArtifactFiles(ctx.routes, outputDirectory);
516
- writeLlmsTxt(ctx.routes, ctx.projectConfig, outputDirectory);
517
- }
518
- }
519
- };
520
- }
521
- //#endregion
522
- //#region source/plugins/html-shell/index.ts
523
- var THEME_BOOTSTRAP_SCRIPT = `(function(){try{var e=localStorage.getItem('clarify:theme');var t=e==='dark'||e==='light'||e==='system'?e:'system';var r=t==='system'?window.matchMedia('(prefers-color-scheme: dark)').matches:t==='dark';document.documentElement.classList.toggle('dark',r);document.documentElement.style.colorScheme=r?'dark':'light'}catch(e){}})();`;
524
- var REL_ICON_RE = /<link\s+[^>]*rel=["'][^"']*\bicon\b[^"']*["'][^>]*>/i;
525
- function inferIconType(href) {
526
- const path = href.split(/[?#]/, 1)[0]?.toLowerCase() ?? href.toLowerCase();
527
- if (path.endsWith(".svg")) return "image/svg+xml";
528
- if (path.endsWith(".png")) return "image/png";
529
- if (path.endsWith(".ico")) return "image/x-icon";
530
- if (path.endsWith(".webp")) return "image/webp";
531
- }
532
- function createIconTag(href, media) {
533
- return {
534
- tag: "link",
535
- attrs: {
536
- rel: "icon",
537
- ...inferIconType(href) ? { type: inferIconType(href) } : {},
538
- href,
539
- ...media ? { media } : {}
540
- },
541
- injectTo: "head"
542
- };
543
- }
544
- function createIconTags(icon) {
545
- if (!icon) return [];
546
- if (typeof icon === "string") return [createIconTag(icon)];
547
- const lightIcon = icon.light ?? icon.dark;
548
- const darkIcon = icon.dark ?? icon.light;
549
- if (!lightIcon || !darkIcon) return [];
550
- if (lightIcon === darkIcon) return [createIconTag(lightIcon)];
551
- return [createIconTag(lightIcon, "(prefers-color-scheme: light)"), createIconTag(darkIcon, "(prefers-color-scheme: dark)")];
552
- }
553
- function createHtmlShellPlugin() {
554
- return {
555
- name: "clarify:html-shell",
556
- hooks: { "html:transform": (input, ctx) => ({
557
- html: input.html,
558
- tags: [
559
- ...input.tags,
560
- ...REL_ICON_RE.test(input.html) ? [] : createIconTags(ctx.projectConfig.favicon ?? ctx.projectConfig.logo),
561
- {
562
- tag: "script",
563
- children: THEME_BOOTSTRAP_SCRIPT,
564
- injectTo: "head-prepend"
565
- },
566
- {
567
- tag: "script",
568
- attrs: {
569
- type: "module",
570
- src: input.clientEntryId
571
- },
572
- injectTo: "body"
573
- }
574
- ],
575
- clientEntryId: input.clientEntryId,
576
- dev: input.dev
577
- }) }
578
- };
570
+ sections: route?.sections ? navigationSections(route.sections) : void 0
571
+ };
572
+ });
573
+ return {
574
+ path: children[0]?.path ?? localizedRoutePath("/", locale, i18n),
575
+ title: resolveLocalizedText(group.group, locale, i18n.defaultLocale) ?? "",
576
+ icon: group.icon,
577
+ children
578
+ };
579
+ });
580
+ }
581
+ function buildLocalizedNavigationFromTabsConfig(routes, tabs, i18n) {
582
+ if (!i18n) return void 0;
583
+ const result = {};
584
+ for (const locale of i18n.locales) {
585
+ const localeRoutes = routes.filter((route) => route.locale === locale.code);
586
+ result[locale.code] = { tabs: tabs.map((tab) => {
587
+ const children = buildLocalizedNavigationForLocale(localeRoutes, tab.pages, locale.code, i18n);
588
+ return {
589
+ type: "tab",
590
+ path: firstNavigationPath(children),
591
+ title: resolveLocalizedText(tab.tab, locale.code, i18n.defaultLocale) ?? "",
592
+ icon: tab.icon,
593
+ children
594
+ };
595
+ }) };
596
+ }
597
+ return result;
579
598
  }
580
599
  //#endregion
581
600
  //#region source/plugins/openapi/parser.ts
@@ -620,21 +639,26 @@ function findOpenAPIRoutes(dir, base = dir) {
620
639
  }
621
640
  return routes;
622
641
  }
642
+ function operationMatchesTags(operationTags, filterTags) {
643
+ if (!filterTags?.length) return true;
644
+ return operationTags?.some((tag) => filterTags.includes(tag)) ?? false;
645
+ }
623
646
  /** Extract endpoint operations from an OpenAPI spec as page sections. */
624
- function extractOpenAPISections(spec) {
647
+ function extractOpenAPISections(spec, filterTags) {
625
648
  const sections = [];
626
649
  const paths = spec.paths ?? {};
627
650
  for (const [path, pathItem] of Object.entries(paths)) {
628
651
  if (!pathItem) continue;
629
652
  for (const method of OPENAPI_HTTP_METHODS) {
630
653
  const op = pathItem[method];
631
- if (!op) continue;
654
+ if (!op || !operationMatchesTags(op.tags, filterTags)) continue;
632
655
  const title = op.summary ?? `${method.toUpperCase()} ${path}`;
633
656
  sections.push({
634
657
  id: (0, github_slugger.slug)(`${method} ${path}`),
635
658
  title,
636
659
  level: 2,
637
- badge: method.toUpperCase()
660
+ badge: method.toUpperCase(),
661
+ tags: op.tags
638
662
  });
639
663
  }
640
664
  }
@@ -659,17 +683,89 @@ async function readOpenAPISpec(filePath) {
659
683
  }
660
684
  }
661
685
  //#endregion
686
+ //#region source/core/virtual-modules.ts
687
+ var VIRTUAL_CONFIG = "virtual:clarify-config";
688
+ var VIRTUAL_ROUTES = "virtual:clarify-routes";
689
+ var VIRTUAL_SERVER_ROUTES = "virtual:clarify-routes/server";
690
+ var VIRTUAL_RUNTIME = "virtual:clarify-runtime";
691
+ var VIRTUAL_CLIENT_ENTRY = "virtual:clarify-entry-client";
692
+ var RESOLVED_CLIENT_ENTRY = "\0virtual:clarify-entry-client";
693
+ function resolveVirtualId(id) {
694
+ return "\0" + id;
695
+ }
696
+ function stripVirtualPrefix(id) {
697
+ return id.startsWith("\0") ? id.slice(1) : id;
698
+ }
699
+ function generateConfigModule(projectConfig, buildOptions) {
700
+ return `export const config = ${JSON.stringify({
701
+ ...projectConfig,
702
+ ...buildOptions
703
+ })};`;
704
+ }
705
+ function moduleSpecifier(value) {
706
+ return JSON.stringify(value);
707
+ }
708
+ function generateRoutesModule(routes, resolvedNavigation, projectConfig, mode = "client") {
709
+ const imports = mode === "server" ? routes.map((r, i) => `import Page${i} from ${moduleSpecifier(r.virtualModuleId)};`).join("\n") : "";
710
+ const routesArray = routes.map((r, i) => {
711
+ const sections = r.sections && r.sections.length > 0 ? `, sections: ${JSON.stringify(r.sections.map((s) => ({
712
+ id: s.id,
713
+ title: s.title,
714
+ badge: s.badge,
715
+ tags: s.tags
716
+ })))}` : "";
717
+ const contentArtifactUrl = r.contentArtifactUrl ? `, contentArtifactUrl: ${JSON.stringify(r.contentArtifactUrl)}` : "";
718
+ const basePath = r.basePath ? `, basePath: ${JSON.stringify(r.basePath)}` : "";
719
+ const locale = r.locale ? `, locale: ${JSON.stringify(r.locale)}` : "";
720
+ const isFallback = r.isFallback ? ", isFallback: true" : "";
721
+ const alternates = r.alternates ? `, alternates: ${JSON.stringify(r.alternates)}` : "";
722
+ const description = r.description ? `, description: ${JSON.stringify(r.description)}` : "";
723
+ const keywords = r.keywords && r.keywords.length > 0 ? `, keywords: ${JSON.stringify(r.keywords)}` : "";
724
+ const sourceUrl = r.sourceUrl ? `, sourceUrl: ${JSON.stringify(r.sourceUrl)}` : "";
725
+ const component = mode === "server" ? `Page${i}` : `() => import(${moduleSpecifier(r.virtualModuleId)})`;
726
+ const lazy = mode === "client" ? ", lazy: true" : "";
727
+ return ` { path: ${JSON.stringify(r.path)}, title: ${JSON.stringify(r.title)}, component: ${component}${lazy}, kind: ${JSON.stringify(r.kind)}${basePath}${locale}${isFallback}${alternates}${description}${keywords}${sections}${contentArtifactUrl}${sourceUrl} }`;
728
+ }).join(",\n");
729
+ const navigation = resolvedNavigation ?? (projectConfig?.tabs ? projectConfig.i18n ? buildLocalizedNavigationFromTabsConfig(routes, projectConfig.tabs, projectConfig.i18n) ?? {} : buildNavigationFromTabsConfig(routes, projectConfig.tabs) : buildNavigation(routes));
730
+ return `${imports}\n\nexport const routes = [\n${routesArray}\n];\n\nexport const navigation = ${JSON.stringify(navigation, null, 2)};\n`;
731
+ }
732
+ function createRuntimeModule() {
733
+ return "export const openApis = {};";
734
+ }
735
+ function createClientEntryModule(options = {}) {
736
+ return `
737
+ import '@clarify-labs/renderer/style.css';
738
+ import { render } from '@clarify-labs/renderer/client';
739
+ import { routes, navigation } from '${VIRTUAL_ROUTES}';
740
+ import { config } from '${VIRTUAL_CONFIG}';
741
+ import { openApis } from '${VIRTUAL_RUNTIME}';
742
+ render({ config, routes, navigation, openApis, themeEditor: ${JSON.stringify(options.themeEditor ?? false)} });`;
743
+ }
744
+ function buildVirtualModules(args) {
745
+ const modules = /* @__PURE__ */ new Map();
746
+ const clientEntryModule = createClientEntryModule({ themeEditor: args.themeEditor });
747
+ modules.set(VIRTUAL_CONFIG, generateConfigModule(args.projectConfig, args.generateOptions));
748
+ modules.set(VIRTUAL_ROUTES, generateRoutesModule(args.routes, args.navigation, args.projectConfig, "client"));
749
+ modules.set(VIRTUAL_SERVER_ROUTES, generateRoutesModule(args.routes, args.navigation, args.projectConfig, "server"));
750
+ modules.set(VIRTUAL_RUNTIME, createRuntimeModule());
751
+ modules.set(VIRTUAL_CLIENT_ENTRY, clientEntryModule);
752
+ modules.set(RESOLVED_CLIENT_ENTRY, clientEntryModule);
753
+ for (const route of args.routes) modules.set(route.virtualModuleId, `export { default } from ${moduleSpecifier(route.filePath)};`);
754
+ return modules;
755
+ }
756
+ //#endregion
662
757
  //#region source/plugins/openapi/virtual-modules.ts
663
- var openApiRegistryModuleId = "virtual:clarify-openapi-registry";
758
+ var openApiRegistryModuleId = VIRTUAL_RUNTIME;
664
759
  function generateOpenAPIRegistryModule(openApis) {
665
760
  return `export const openApis = ${JSON.stringify(openApis)};`;
666
761
  }
667
- function generateOpenAPIModule(spec) {
762
+ function generateOpenAPIModule(spec, tagFilter) {
668
763
  return `import { createElement } from 'react';
669
764
  import { OpenApiDocument } from '@clarify-labs/renderer';
670
765
  const spec = ${JSON.stringify(spec)};
766
+ const tagFilter = ${JSON.stringify(tagFilter)};
671
767
  export default function OpenApiRoutePage() {
672
- return createElement(OpenApiDocument, { spec });
768
+ return createElement(OpenApiDocument, { spec, tagFilter });
673
769
  }`;
674
770
  }
675
771
  function generateOpenAPIErrorModule(diagnostic) {
@@ -695,6 +791,55 @@ export default function OpenApiErrorRoutePage() {
695
791
  }
696
792
  //#endregion
697
793
  //#region source/plugins/openapi/index.ts
794
+ function collectOpenAPIPageItems(config) {
795
+ const items = [];
796
+ function visitPages(pages) {
797
+ if (!pages || pages === "FileTree") return;
798
+ for (const group of pages) for (const item of group.pages) if (typeof item !== "string" && "openapi" in item && item.filter?.tags?.length) items.push(item);
799
+ }
800
+ for (const tab of config.tabs ?? []) visitPages(tab.pages);
801
+ return items;
802
+ }
803
+ function withAlternates$1(route, routes, i18n) {
804
+ const basePath = route.basePath ?? route.path;
805
+ const routeByLocaleAndBase = new Map(routes.map((route) => [`${route.locale ?? ""}:${route.basePath ?? route.path}`, route]));
806
+ const alternates = Object.fromEntries(i18n.locales.flatMap((locale) => {
807
+ const alternate = routeByLocaleAndBase.get(`${locale.code}:${basePath}`);
808
+ return alternate ? [[locale.code, alternate.path]] : [];
809
+ }));
810
+ return {
811
+ ...route,
812
+ alternates
813
+ };
814
+ }
815
+ function createTaggedOpenAPIRoutes(routes, config) {
816
+ const pageItems = collectOpenAPIPageItems(config);
817
+ if (!pageItems.length) return routes;
818
+ const additions = [];
819
+ const existingKeys = new Set(routes.map((route) => `${route.locale ?? ""}:${route.basePath ?? route.path}`));
820
+ for (const item of pageItems) {
821
+ const tagFilter = item.filter?.tags?.filter(Boolean);
822
+ if (!tagFilter?.length) continue;
823
+ const sourceBasePath = openAPIPagePathFromRef(item.openapi);
824
+ const filteredBasePath = openAPIPagePathFromRef(item.openapi, tagFilter);
825
+ for (const route of routes) {
826
+ if (route.kind !== "openapi" || (route.basePath ?? route.path) !== sourceBasePath) continue;
827
+ const key = `${route.locale ?? ""}:${filteredBasePath}`;
828
+ if (existingKeys.has(key)) continue;
829
+ existingKeys.add(key);
830
+ additions.push({
831
+ ...route,
832
+ path: route.locale && config.i18n ? localizedRoutePath(filteredBasePath, route.locale, config.i18n) : filteredBasePath,
833
+ basePath: filteredBasePath,
834
+ title: route.title,
835
+ virtualModuleId: `${route.virtualModuleId}/${openAPITagsPathSegment(tagFilter)}`,
836
+ openapiTagFilter: tagFilter
837
+ });
838
+ }
839
+ }
840
+ const nextRoutes = [...routes, ...additions];
841
+ return config.i18n ? nextRoutes.map((route) => withAlternates$1(route, nextRoutes, config.i18n)) : nextRoutes;
842
+ }
698
843
  function createOpenAPIPlugin() {
699
844
  const specs = {};
700
845
  return {
@@ -704,10 +849,16 @@ function createOpenAPIPlugin() {
704
849
  ...input,
705
850
  routes: [...input.routes, ...findOpenAPIRoutes(input.contentRoot)]
706
851
  }),
707
- "routes:discovered": async (routes) => {
852
+ "routes:discovered": async (routes, ctx) => {
708
853
  for (const key of Object.keys(specs)) delete specs[key];
709
- for (const route of routes.filter((route) => route.kind === "openapi")) {
710
- const result = await readOpenAPISpec(route.filePath);
854
+ const nextRoutes = createTaggedOpenAPIRoutes(routes, ctx.projectConfig);
855
+ const specByFilePath = /* @__PURE__ */ new Map();
856
+ for (const route of nextRoutes.filter((route) => route.kind === "openapi")) {
857
+ const specFromCache = specByFilePath.get(route.filePath);
858
+ const result = specFromCache ? {
859
+ ok: true,
860
+ spec: specFromCache
861
+ } : await readOpenAPISpec(route.filePath);
711
862
  if (!result.ok) {
712
863
  route.diagnostic = result.diagnostic;
713
864
  route.title = route.title || "OpenAPI parse error";
@@ -715,17 +866,24 @@ function createOpenAPIPlugin() {
715
866
  continue;
716
867
  }
717
868
  const spec = result.spec;
718
- specs[route.virtualModuleId] = spec;
719
- specs[`virtual:clarify-page/${route.path.replace(/^\//, "")}`] = spec;
869
+ specByFilePath.set(route.filePath, spec);
870
+ specs[route.virtualModuleId] = {
871
+ spec,
872
+ tagFilter: route.openapiTagFilter
873
+ };
874
+ specs[`virtual:clarify-page/${route.path.replace(/^\//, "")}`] = {
875
+ spec,
876
+ tagFilter: route.openapiTagFilter
877
+ };
720
878
  route.title = spec.info?.title ?? route.title;
721
879
  route.description = spec.info?.description ?? route.description;
722
- route.sections = extractOpenAPISections(spec);
880
+ route.sections = extractOpenAPISections(spec, route.openapiTagFilter);
723
881
  }
724
- return routes;
882
+ return nextRoutes;
725
883
  },
726
884
  "modules:before": (modules, ctx) => {
727
- modules.set(openApiRegistryModuleId, generateOpenAPIRegistryModule(specs));
728
- for (const [moduleId, spec] of Object.entries(specs)) modules.set(moduleId, generateOpenAPIModule(spec));
885
+ modules.set(openApiRegistryModuleId, generateOpenAPIRegistryModule(Object.fromEntries(Object.entries(specs).map(([moduleId, entry]) => [moduleId, entry.spec]))));
886
+ for (const [moduleId, entry] of Object.entries(specs)) modules.set(moduleId, generateOpenAPIModule(entry.spec, entry.tagFilter));
729
887
  for (const route of ctx.routes.filter((route) => route.kind === "openapi" && route.diagnostic)) if (route.diagnostic) modules.set(route.virtualModuleId, generateOpenAPIErrorModule(route.diagnostic));
730
888
  return modules;
731
889
  }
@@ -733,6 +891,107 @@ function createOpenAPIPlugin() {
733
891
  };
734
892
  }
735
893
  //#endregion
894
+ //#region source/plugins/seo/artifacts.ts
895
+ function trimTrailingSlash(value) {
896
+ return value.replace(/\/+$/, "");
897
+ }
898
+ function normalizeRoutePrefix(routePrefix) {
899
+ if (!routePrefix || routePrefix === "/") return "";
900
+ return `/${routePrefix.replace(/^\/+|\/+$/g, "")}`;
901
+ }
902
+ function routeUrl(siteUrl, routePrefix, routePath) {
903
+ const base = trimTrailingSlash(siteUrl);
904
+ return `${base}${normalizeRoutePrefix(routePrefix)}${routePath === "/" ? "" : routePath.replace(/^\/+/, "/")}` || base;
905
+ }
906
+ function escapeXml(value) {
907
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
908
+ }
909
+ function publicRoutes(routes) {
910
+ return routes.filter((route) => route.path !== "/404");
911
+ }
912
+ function createSitemapXml(routes, config) {
913
+ if (!config.siteUrl) return void 0;
914
+ return [
915
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
916
+ "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">",
917
+ ...publicRoutes(routes).map((route) => routeUrl(config.siteUrl, config.routePrefix, route.path)).filter((url, index, list) => list.indexOf(url) === index).sort().map((url) => ` <url><loc>${escapeXml(url)}</loc></url>`),
918
+ "</urlset>",
919
+ ""
920
+ ].join("\n");
921
+ }
922
+ function createRobotsTxt(config) {
923
+ if (!config.siteUrl) return void 0;
924
+ return [
925
+ "User-agent: *",
926
+ "Allow: /",
927
+ `Sitemap: ${routeUrl(config.siteUrl, config.routePrefix, "/sitemap.xml")}`,
928
+ ""
929
+ ].join("\n");
930
+ }
931
+ async function writeSeoFiles(outputRoot, routes, config) {
932
+ const sitemap = createSitemapXml(routes, config);
933
+ const robots = createRobotsTxt(config);
934
+ if (!sitemap || !robots) return;
935
+ await (0, node_fs_promises.mkdir)(outputRoot, { recursive: true });
936
+ await Promise.all([(0, node_fs_promises.writeFile)((0, node_path.join)(outputRoot, "sitemap.xml"), sitemap, "utf-8"), (0, node_fs_promises.writeFile)((0, node_path.join)(outputRoot, "robots.txt"), robots, "utf-8")]);
937
+ }
938
+ //#endregion
939
+ //#region source/plugins/seo/index.ts
940
+ function createSeoPlugin() {
941
+ return {
942
+ name: "clarify:seo",
943
+ hooks: { async "build:done"(ctx) {
944
+ const outputDirectory = ctx.generateOptions.outputDirectory;
945
+ if (!outputDirectory) return;
946
+ await writeSeoFiles((0, node_path.join)(ctx.generateOptions.projectRoot, outputDirectory), ctx.routes, ctx.projectConfig);
947
+ } }
948
+ };
949
+ }
950
+ //#endregion
951
+ //#region source/plugins/source-links/source-links.ts
952
+ function trimSlashes(value) {
953
+ return value.replace(/^\/+|\/+$/g, "");
954
+ }
955
+ function normalizeRepositoryUrl(repository) {
956
+ return repository.replace(/\.git$/, "").replace(/\/+$/, "");
957
+ }
958
+ function encodePath(path) {
959
+ return path.split("/").filter(Boolean).map((segment) => encodeURIComponent(segment)).join("/");
960
+ }
961
+ function createSourceUrl(filePath, contentRoot, source) {
962
+ const repository = normalizeRepositoryUrl(source.repository);
963
+ const branch = source.branch ?? "main";
964
+ const sourcePath = [source.directory ? trimSlashes(source.directory) : "", (0, node_path.relative)(contentRoot, filePath).replaceAll("\\", "/")].filter(Boolean).join("/");
965
+ return `${repository}/edit/${encodeURIComponent(branch)}/${encodePath(sourcePath)}`;
966
+ }
967
+ function attachSourceUrls(routes, contentRoot, source) {
968
+ if (!source) return;
969
+ for (const route of routes) route.sourceUrl = createSourceUrl(route.filePath, contentRoot, source);
970
+ }
971
+ //#endregion
972
+ //#region source/plugins/source-links/index.ts
973
+ function createSourceLinksPlugin() {
974
+ return {
975
+ name: "clarify:source-links",
976
+ hooks: { "routes:discovered": (routes, ctx) => {
977
+ attachSourceUrls(routes, (0, node_path.join)(ctx.generateOptions.projectRoot, ctx.generateOptions.rootDirectory), ctx.projectConfig.source);
978
+ return routes;
979
+ } }
980
+ };
981
+ }
982
+ //#endregion
983
+ //#region source/core/builtin.ts
984
+ function createBuiltinPlugins(options = {}) {
985
+ const plugins = [
986
+ createOpenAPIPlugin(),
987
+ createSourceLinksPlugin(),
988
+ createContentArtifactsPlugin(),
989
+ createSeoPlugin()
990
+ ];
991
+ if (options.htmlShell ?? true) plugins.push(createHtmlShellPlugin());
992
+ return plugins;
993
+ }
994
+ //#endregion
736
995
  //#region source/core/config-schema.ts
737
996
  var clarifyLogoConfigSchema = zod.z.union([zod.z.string(), zod.z.object({
738
997
  light: zod.z.string().optional(),
@@ -790,6 +1049,11 @@ var clarifyFooterConfigSchema = zod.z.object({
790
1049
  socials: zod.z.record(zod.z.string(), zod.z.string()).optional(),
791
1050
  copyright: clarifyLocalizedTextSchema.optional()
792
1051
  });
1052
+ var clarifySourceConfigSchema = zod.z.object({
1053
+ repository: zod.z.string(),
1054
+ branch: zod.z.string().optional(),
1055
+ directory: zod.z.string().optional()
1056
+ });
793
1057
  var clarifyPagesItemSchema = zod.z.union([
794
1058
  zod.z.string(),
795
1059
  zod.z.object({
@@ -801,7 +1065,8 @@ var clarifyPagesItemSchema = zod.z.union([
801
1065
  zod.z.object({
802
1066
  openapi: zod.z.string(),
803
1067
  icon: zod.z.string().optional(),
804
- title: clarifyLocalizedTextSchema.optional()
1068
+ title: clarifyLocalizedTextSchema.optional(),
1069
+ filter: zod.z.object({ tags: zod.z.array(zod.z.string()).optional() }).optional()
805
1070
  })
806
1071
  ]);
807
1072
  var clarifyPagesGroupSchema = zod.z.object({
@@ -817,15 +1082,20 @@ var clarifyTabItemSchema = zod.z.object({
817
1082
  });
818
1083
  var clarifyTabsConfigSchema = zod.z.array(clarifyTabItemSchema);
819
1084
  var clarifyThemePresetSchema = zod.z.enum(["default", "base"]);
1085
+ var clarifyThemeModeColorValueSchema = zod.z.object({
1086
+ light: zod.z.string().optional(),
1087
+ dark: zod.z.string().optional()
1088
+ });
1089
+ var clarifyThemeColorValueSchema = zod.z.union([zod.z.string(), clarifyThemeModeColorValueSchema]);
820
1090
  var clarifyThemeColorTokensConfigSchema = zod.z.object({
821
- primary: zod.z.string().optional(),
822
- accent: zod.z.string().optional(),
823
- background: zod.z.string().optional(),
824
- foreground: zod.z.string().optional(),
825
- surface: zod.z.string().optional(),
826
- muted: zod.z.string().optional(),
827
- border: zod.z.string().optional(),
828
- codeBackground: zod.z.string().optional()
1091
+ primary: clarifyThemeColorValueSchema.optional(),
1092
+ accent: clarifyThemeColorValueSchema.optional(),
1093
+ background: clarifyThemeColorValueSchema.optional(),
1094
+ foreground: clarifyThemeColorValueSchema.optional(),
1095
+ surface: clarifyThemeColorValueSchema.optional(),
1096
+ muted: clarifyThemeColorValueSchema.optional(),
1097
+ border: clarifyThemeColorValueSchema.optional(),
1098
+ codeBackground: clarifyThemeColorValueSchema.optional()
829
1099
  });
830
1100
  var clarifyThemeRadiusTokensConfigSchema = zod.z.object({
831
1101
  sm: zod.z.string().optional(),
@@ -841,11 +1111,14 @@ var clarifyThemeLayoutConfigSchema = zod.z.object({ maxWidth: zod.z.string().opt
841
1111
  var clarifyThemeConfigSchema = zod.z.object({
842
1112
  preset: clarifyThemePresetSchema.optional(),
843
1113
  tokens: clarifyThemeTokensConfigSchema.optional(),
844
- layout: clarifyThemeLayoutConfigSchema.optional()
1114
+ layout: clarifyThemeLayoutConfigSchema.optional(),
1115
+ editor: zod.z.boolean().optional()
845
1116
  });
846
1117
  var clarifyProjectConfigSchema = zod.z.object({
847
1118
  title: zod.z.string().optional(),
848
1119
  description: zod.z.string().optional(),
1120
+ siteUrl: zod.z.string().optional(),
1121
+ source: clarifySourceConfigSchema.optional(),
849
1122
  logo: clarifyLogoConfigSchema.optional(),
850
1123
  favicon: clarifyFaviconConfigSchema.optional(),
851
1124
  theme: clarifyThemeConfigSchema.optional(),
@@ -863,14 +1136,14 @@ var clarifyThemePresets = {
863
1136
  preset: "default",
864
1137
  tokens: {
865
1138
  colors: {
866
- primary: "#00D492",
867
- accent: "#00F6C9",
1139
+ primary: "#047857",
1140
+ accent: "#0D9488",
868
1141
  background: "#ffffff",
869
1142
  foreground: "#111827",
870
1143
  surface: "#ffffff",
871
1144
  muted: "#64748b",
872
1145
  border: "rgb(15 23 42 / 0.12)",
873
- codeBackground: "#f7fefb"
1146
+ codeBackground: "#f6fbf8"
874
1147
  },
875
1148
  radius: {
876
1149
  sm: "6px",
@@ -879,7 +1152,8 @@ var clarifyThemePresets = {
879
1152
  xl: "18px"
880
1153
  }
881
1154
  },
882
- layout: { maxWidth: "82rem" }
1155
+ layout: { maxWidth: "82rem" },
1156
+ editor: false
883
1157
  },
884
1158
  base: {
885
1159
  preset: "base",
@@ -901,7 +1175,8 @@ var clarifyThemePresets = {
901
1175
  xl: "16px"
902
1176
  }
903
1177
  },
904
- layout: { maxWidth: "80rem" }
1178
+ layout: { maxWidth: "80rem" },
1179
+ editor: false
905
1180
  }
906
1181
  };
907
1182
  function resolveThemeConfig(theme = {}) {
@@ -922,7 +1197,8 @@ function resolveThemeConfig(theme = {}) {
922
1197
  layout: {
923
1198
  ...presetTheme.layout,
924
1199
  ...theme.layout
925
- }
1200
+ },
1201
+ editor: theme.editor ?? presetTheme.editor
926
1202
  };
927
1203
  }
928
1204
  //#endregion
@@ -961,6 +1237,8 @@ function resolveProjectConfig(config = {}) {
961
1237
  return {
962
1238
  title: config.title ?? "Clarify Docs",
963
1239
  description: config.description ?? "",
1240
+ siteUrl: config.siteUrl,
1241
+ source: config.source,
964
1242
  logo: config.logo,
965
1243
  favicon: config.favicon,
966
1244
  routePrefix: config.routePrefix ?? "/",
@@ -1013,12 +1291,104 @@ async function runBuildDoneHooks(plugins, ctx) {
1013
1291
  //#region source/core/options.ts
1014
1292
  function resolveBuildOptions(options = {}) {
1015
1293
  return {
1294
+ projectRoot: options.projectRoot ?? process.cwd(),
1016
1295
  rootDirectory: options.rootDirectory ?? "source",
1017
1296
  outputDirectory: options.outputDirectory,
1018
1297
  ssg: { failOnError: options.ssg?.failOnError ?? true }
1019
1298
  };
1020
1299
  }
1021
1300
  //#endregion
1301
+ //#region source/core/site.ts
1302
+ function withAlternates(route, routeList, i18n) {
1303
+ const basePath = route.basePath ?? route.path;
1304
+ const routeByLocaleAndBase = new Map(routeList.map((route) => [`${route.locale ?? ""}:${route.basePath ?? route.path}`, route]));
1305
+ const alternates = Object.fromEntries(i18n.locales.flatMap((locale) => {
1306
+ const alternate = routeByLocaleAndBase.get(`${locale.code}:${basePath}`);
1307
+ return alternate ? [[locale.code, alternate.path]] : [];
1308
+ }));
1309
+ return {
1310
+ ...route,
1311
+ alternates
1312
+ };
1313
+ }
1314
+ async function discoverRoutesForRoot(routeRoot, locale, plugins, ctx) {
1315
+ return (await runHooks(plugins, "routes:discover", {
1316
+ contentRoot: routeRoot,
1317
+ locale,
1318
+ routes: findContentRoutes(routeRoot)
1319
+ }, ctx)).routes;
1320
+ }
1321
+ async function discoverRoutes(root, contentRoot, plugins, ctx) {
1322
+ const i18n = ctx.projectConfig.i18n;
1323
+ if (!i18n) return discoverRoutesForRoot(contentRoot, void 0, plugins, ctx);
1324
+ const localizedRoutes = [];
1325
+ for (const locale of i18n.locales) {
1326
+ const discovered = await discoverRoutesForRoot((0, node_path.join)(contentRoot, locale.code), locale.code, plugins, ctx);
1327
+ for (const route of discovered) {
1328
+ const basePath = route.basePath ?? route.path;
1329
+ localizedRoutes.push({
1330
+ ...route,
1331
+ path: localizedRoutePath(basePath, locale.code, i18n),
1332
+ basePath,
1333
+ locale: locale.code,
1334
+ virtualModuleId: virtualModuleIdFromRef((0, node_path.relative)(contentRoot, route.filePath))
1335
+ });
1336
+ }
1337
+ }
1338
+ if (i18n.missing === "fallback") {
1339
+ const routeByLocaleAndBase = new Map(localizedRoutes.map((route) => [`${route.locale ?? ""}:${route.basePath ?? route.path}`, route]));
1340
+ const defaultRoutes = localizedRoutes.filter((route) => route.locale === i18n.defaultLocale);
1341
+ for (const sourceRoute of defaultRoutes) {
1342
+ const basePath = sourceRoute.basePath ?? sourceRoute.path;
1343
+ for (const locale of i18n.locales) {
1344
+ const key = `${locale.code}:${basePath}`;
1345
+ if (routeByLocaleAndBase.has(key)) continue;
1346
+ localizedRoutes.push({
1347
+ ...sourceRoute,
1348
+ path: localizedRoutePath(basePath, locale.code, i18n),
1349
+ locale: locale.code,
1350
+ isFallback: true
1351
+ });
1352
+ }
1353
+ }
1354
+ }
1355
+ return localizedRoutes.map((route) => withAlternates(route, localizedRoutes, i18n));
1356
+ }
1357
+ async function resolveClarifySite(options = {}, resolveOptions = {}) {
1358
+ const root = options.projectRoot ?? process.cwd();
1359
+ const projectConfig = resolveProjectConfig(options);
1360
+ const generateOptions = resolveBuildOptions(options);
1361
+ const contentRoot = (0, node_path.join)(root, generateOptions.rootDirectory);
1362
+ const plugins = [...createBuiltinPlugins({ htmlShell: resolveOptions.includeHtmlShellPlugin }), ...options.plugins ?? []];
1363
+ const ctx = {
1364
+ projectConfig,
1365
+ generateOptions,
1366
+ routes: [],
1367
+ navigation: []
1368
+ };
1369
+ let routes = await discoverRoutes(root, contentRoot, plugins, ctx);
1370
+ routes = await runHooks(plugins, "routes:discovered", routes, ctx);
1371
+ const defaultNavigation = projectConfig.tabs ? projectConfig.i18n ? buildLocalizedNavigationFromTabsConfig(routes, projectConfig.tabs, projectConfig.i18n) ?? {} : buildNavigationFromTabsConfig(routes, projectConfig.tabs) : buildNavigation(routes);
1372
+ const resolved = await runHooks(plugins, "routes:resolved", {
1373
+ routes,
1374
+ navigation: defaultNavigation
1375
+ }, ctx);
1376
+ routes = resolved.routes;
1377
+ const navigation = resolved.navigation;
1378
+ ctx.routes = routes;
1379
+ ctx.navigation = navigation;
1380
+ return {
1381
+ root,
1382
+ contentRoot,
1383
+ projectConfig,
1384
+ generateOptions,
1385
+ plugins,
1386
+ ctx,
1387
+ routes,
1388
+ navigation
1389
+ };
1390
+ }
1391
+ //#endregion
1022
1392
  //#region source/core/runtime-deps.ts
1023
1393
  var requireFromCli = (0, node_module.createRequire)({}.url);
1024
1394
  function resolveCliDependency(id) {
@@ -1113,13 +1483,21 @@ function injectSSRIntoTemplate(template, appHtml, projectConfig, route) {
1113
1483
  html = html.replace(/<div id="root">[\s\S]*?<\/div>/, `<div id="root">${appHtml}</div>`);
1114
1484
  return html;
1115
1485
  }
1486
+ function isNotFoundRoute(route) {
1487
+ return (route.basePath ?? route.path) === "/404";
1488
+ }
1489
+ function routeOutputFiles(outputDirectory, route) {
1490
+ const files = [(0, node_path.join)(outputDirectory, route.path, "index.html")];
1491
+ if (isNotFoundRoute(route) && (!route.locale || route.path === "/404")) files.push((0, node_path.join)(outputDirectory, "404.html"));
1492
+ return files;
1493
+ }
1116
1494
  var SSR_ENTRY_CODE = `import { renderToHTML } from '@clarify-labs/renderer/server';
1117
1495
  import { routes, navigation } from 'virtual:clarify-routes/server';
1118
1496
  import { config } from 'virtual:clarify-config';
1119
- import { openApis } from 'virtual:clarify-openapi-registry';
1497
+ import { openApis } from 'virtual:clarify-runtime';
1120
1498
 
1121
1499
  export function render(url) {
1122
- return renderToHTML({ config, routes, navigation, openApis, url });
1500
+ return renderToHTML({ config, routes, navigation, openApis, url, themeEditor: config.theme.editor });
1123
1501
  }`;
1124
1502
  function createTempEntryFile(content) {
1125
1503
  const entryPath = (0, node_path.join)((0, node_fs.mkdtempSync)((0, node_path.join)((0, node_os.tmpdir)(), "clarify-ssr-")), "entry-server.ts");
@@ -1150,9 +1528,10 @@ async function renderSSGRoutes(routes, projectConfig, outputDirectory, ssrBundle
1150
1528
  if (!template) throw new Error(`[clarify] index.html not found in outputDirectory "${outputDirectory}". Make sure Vite build produces it.`);
1151
1529
  for (const route of routes) try {
1152
1530
  const finalHtml = injectSSRIntoTemplate(template, render(route.path), projectConfig, route);
1153
- const outFile = (0, node_path.join)(outputDirectory, route.path, "index.html");
1154
- (0, node_fs.mkdirSync)((0, node_path.dirname)(outFile), { recursive: true });
1155
- (0, node_fs.writeFileSync)(outFile, finalHtml, "utf-8");
1531
+ for (const outFile of routeOutputFiles(outputDirectory, route)) {
1532
+ (0, node_fs.mkdirSync)((0, node_path.dirname)(outFile), { recursive: true });
1533
+ (0, node_fs.writeFileSync)(outFile, finalHtml, "utf-8");
1534
+ }
1156
1535
  } catch (err) {
1157
1536
  console.error(`[clarify] Failed to render route "${route.path}":`, err);
1158
1537
  if (failOnError) throw err;
@@ -1194,75 +1573,6 @@ async function loadClarifyConfig(root, env) {
1194
1573
  return assertConfigObject((await (0, vite.loadConfigFromFile)(env, configFile, root, "silent"))?.config, configFile);
1195
1574
  }
1196
1575
  //#endregion
1197
- //#region source/core/virtual-modules.ts
1198
- var VIRTUAL_CONFIG = "virtual:clarify-config";
1199
- var VIRTUAL_ROUTES = "virtual:clarify-routes";
1200
- var VIRTUAL_SERVER_ROUTES = "virtual:clarify-routes/server";
1201
- var VIRTUAL_OPENAPI_REGISTRY = openApiRegistryModuleId;
1202
- var VIRTUAL_CLIENT_ENTRY = "virtual:clarify-entry-client";
1203
- var RESOLVED_CLIENT_ENTRY = "\0virtual:clarify-entry-client";
1204
- function resolveVirtualId(id) {
1205
- return "\0" + id;
1206
- }
1207
- function stripVirtualPrefix(id) {
1208
- return id.startsWith("\0") ? id.slice(1) : id;
1209
- }
1210
- function generateConfigModule(projectConfig, buildOptions) {
1211
- return `export const config = ${JSON.stringify({
1212
- ...projectConfig,
1213
- ...buildOptions
1214
- })};`;
1215
- }
1216
- function moduleSpecifier(value) {
1217
- return JSON.stringify(value);
1218
- }
1219
- function generateRoutesModule(routes, resolvedNavigation, projectConfig, mode = "client") {
1220
- const imports = mode === "server" ? routes.map((r, i) => `import Page${i} from ${moduleSpecifier(r.virtualModuleId)};`).join("\n") : "";
1221
- const routesArray = routes.map((r, i) => {
1222
- const sections = r.sections && r.sections.length > 0 ? `, sections: ${JSON.stringify(r.sections.map((s) => ({
1223
- id: s.id,
1224
- title: s.title,
1225
- badge: s.badge,
1226
- tags: s.tags
1227
- })))}` : "";
1228
- const contentArtifactUrl = r.contentArtifactUrl ? `, contentArtifactUrl: ${JSON.stringify(r.contentArtifactUrl)}` : "";
1229
- const basePath = r.basePath ? `, basePath: ${JSON.stringify(r.basePath)}` : "";
1230
- const locale = r.locale ? `, locale: ${JSON.stringify(r.locale)}` : "";
1231
- const isFallback = r.isFallback ? ", isFallback: true" : "";
1232
- const alternates = r.alternates ? `, alternates: ${JSON.stringify(r.alternates)}` : "";
1233
- const description = r.description ? `, description: ${JSON.stringify(r.description)}` : "";
1234
- const keywords = r.keywords && r.keywords.length > 0 ? `, keywords: ${JSON.stringify(r.keywords)}` : "";
1235
- const component = mode === "server" ? `Page${i}` : `() => import(${moduleSpecifier(r.virtualModuleId)})`;
1236
- const lazy = mode === "client" ? ", lazy: true" : "";
1237
- return ` { path: ${JSON.stringify(r.path)}, title: ${JSON.stringify(r.title)}, component: ${component}${lazy}, kind: ${JSON.stringify(r.kind)}${basePath}${locale}${isFallback}${alternates}${description}${keywords}${sections}${contentArtifactUrl} }`;
1238
- }).join(",\n");
1239
- const navigation = resolvedNavigation ?? (projectConfig?.tabs ? projectConfig.i18n ? buildLocalizedNavigationFromTabsConfig(routes, projectConfig.tabs, projectConfig.i18n) ?? {} : buildNavigationFromTabsConfig(routes, projectConfig.tabs) : buildNavigation(routes));
1240
- return `${imports}\n\nexport const routes = [\n${routesArray}\n];\n\nexport const navigation = ${JSON.stringify(navigation, null, 2)};\n`;
1241
- }
1242
- function createClientEntryModule() {
1243
- return `
1244
- import '@clarify-labs/renderer/style.css';
1245
- import { render } from '@clarify-labs/renderer/client';
1246
- import { routes, navigation } from '${VIRTUAL_ROUTES}';
1247
- import { config } from '${VIRTUAL_CONFIG}';
1248
- import { openApis } from '${VIRTUAL_OPENAPI_REGISTRY}';
1249
- render({ config, routes, navigation, openApis });`;
1250
- }
1251
- function buildVirtualModules(args) {
1252
- const modules = /* @__PURE__ */ new Map();
1253
- modules.set(VIRTUAL_CONFIG, generateConfigModule(args.projectConfig, args.generateOptions));
1254
- modules.set(VIRTUAL_ROUTES, generateRoutesModule(args.routes, args.navigation, args.projectConfig, "client"));
1255
- modules.set(VIRTUAL_SERVER_ROUTES, generateRoutesModule(args.routes, args.navigation, args.projectConfig, "server"));
1256
- modules.set(VIRTUAL_OPENAPI_REGISTRY, "export const openApis = {};");
1257
- modules.set(VIRTUAL_CLIENT_ENTRY, createClientEntryModule());
1258
- modules.set(RESOLVED_CLIENT_ENTRY, createClientEntryModule());
1259
- for (const route of args.routes) {
1260
- if (route.kind === "openapi") continue;
1261
- modules.set(route.virtualModuleId, `export { default } from ${moduleSpecifier(route.filePath)};`);
1262
- }
1263
- return modules;
1264
- }
1265
- //#endregion
1266
1576
  //#region source/core/plugin.ts
1267
1577
  function loadVirtualModule(id, modules) {
1268
1578
  const bareId = stripVirtualPrefix(id);
@@ -1275,12 +1585,7 @@ function clarifyPlugin(options = {}) {
1275
1585
  const contentRoot = (0, node_path.join)(root, generateOptions.rootDirectory);
1276
1586
  const configFilePath = findClarifyConfigFile(root);
1277
1587
  let routes = [];
1278
- const clarifyPlugins = [
1279
- createOpenAPIPlugin(),
1280
- createContentArtifactsPlugin(),
1281
- createHtmlShellPlugin(),
1282
- ...options.plugins ?? []
1283
- ];
1588
+ let clarifyPlugins = [...createBuiltinPlugins(), ...options.plugins ?? []];
1284
1589
  const ctx = {
1285
1590
  projectConfig,
1286
1591
  generateOptions,
@@ -1305,71 +1610,15 @@ function clarifyPlugin(options = {}) {
1305
1610
  ctx.projectConfig = projectConfig;
1306
1611
  ctx.generateOptions = generateOptions;
1307
1612
  }
1308
- function withAlternates(route, routeList, i18n) {
1309
- const basePath = route.basePath ?? route.path;
1310
- const routeByLocaleAndBase = new Map(routeList.map((route) => [`${route.locale ?? ""}:${route.basePath ?? route.path}`, route]));
1311
- const alternates = Object.fromEntries(i18n.locales.flatMap((locale) => {
1312
- const alternate = routeByLocaleAndBase.get(`${locale.code}:${basePath}`);
1313
- return alternate ? [[locale.code, alternate.path]] : [];
1314
- }));
1315
- return {
1316
- ...route,
1317
- alternates
1318
- };
1319
- }
1320
- async function discoverRoutesForRoot(routeRoot, locale) {
1321
- return (await runHooks(clarifyPlugins, "routes:discover", {
1322
- contentRoot: routeRoot,
1323
- locale,
1324
- routes: findContentRoutes(routeRoot)
1325
- }, ctx)).routes;
1326
- }
1327
- async function discoverRoutes() {
1328
- const i18n = projectConfig.i18n;
1329
- if (!i18n) return discoverRoutesForRoot(contentRoot);
1330
- const localizedRoutes = [];
1331
- for (const locale of i18n.locales) {
1332
- const discovered = await discoverRoutesForRoot((0, node_path.join)(contentRoot, locale.code), locale.code);
1333
- for (const route of discovered) {
1334
- const basePath = route.basePath ?? route.path;
1335
- localizedRoutes.push({
1336
- ...route,
1337
- path: localizedRoutePath(basePath, locale.code, i18n),
1338
- basePath,
1339
- locale: locale.code,
1340
- virtualModuleId: virtualModuleIdFromRef((0, node_path.relative)(contentRoot, route.filePath))
1341
- });
1342
- }
1343
- }
1344
- if (i18n.missing === "fallback") {
1345
- const routeByLocaleAndBase = new Map(localizedRoutes.map((route) => [`${route.locale ?? ""}:${route.basePath ?? route.path}`, route]));
1346
- const defaultRoutes = localizedRoutes.filter((route) => route.locale === i18n.defaultLocale);
1347
- for (const sourceRoute of defaultRoutes) {
1348
- const basePath = sourceRoute.basePath ?? sourceRoute.path;
1349
- for (const locale of i18n.locales) {
1350
- const key = `${locale.code}:${basePath}`;
1351
- if (routeByLocaleAndBase.has(key)) continue;
1352
- localizedRoutes.push({
1353
- ...sourceRoute,
1354
- path: localizedRoutePath(basePath, locale.code, i18n),
1355
- locale: locale.code,
1356
- isFallback: true
1357
- });
1358
- }
1359
- }
1360
- }
1361
- return localizedRoutes.map((route) => withAlternates(route, localizedRoutes, i18n));
1362
- }
1363
1613
  async function resolveRoutesAndSpecs() {
1364
- routes = await discoverRoutes();
1365
- routes = await runHooks(clarifyPlugins, "routes:discovered", routes, ctx);
1366
- const defaultNavigation = projectConfig.tabs ? projectConfig.i18n ? buildLocalizedNavigationFromTabsConfig(routes, projectConfig.tabs, projectConfig.i18n) ?? {} : buildNavigationFromTabsConfig(routes, projectConfig.tabs) : buildNavigation(routes);
1367
- const resolved = await runHooks(clarifyPlugins, "routes:resolved", {
1368
- routes,
1369
- navigation: defaultNavigation
1370
- }, ctx);
1371
- routes = resolved.routes;
1372
- resolvedNavigation = resolved.navigation;
1614
+ const site = await resolveClarifySite(options);
1615
+ projectConfig = site.projectConfig;
1616
+ generateOptions = site.generateOptions;
1617
+ routes = site.routes;
1618
+ resolvedNavigation = site.navigation;
1619
+ clarifyPlugins = site.plugins;
1620
+ ctx.projectConfig = projectConfig;
1621
+ ctx.generateOptions = generateOptions;
1373
1622
  ctx.routes = routes;
1374
1623
  ctx.navigation = resolvedNavigation;
1375
1624
  }
@@ -1378,7 +1627,8 @@ function clarifyPlugin(options = {}) {
1378
1627
  projectConfig,
1379
1628
  generateOptions,
1380
1629
  routes,
1381
- navigation: resolvedNavigation
1630
+ navigation: resolvedNavigation,
1631
+ themeEditor: viteConfig.command === "serve" || projectConfig.theme.editor
1382
1632
  });
1383
1633
  virtualModules = await runHooks(clarifyPlugins, "modules:before", virtualModules, ctx);
1384
1634
  }
@@ -1458,8 +1708,9 @@ function clarifyPlugin(options = {}) {
1458
1708
  if (id === "virtual:clarify-config" || id === resolveVirtualId("virtual:clarify-config")) return resolveVirtualId(VIRTUAL_CONFIG);
1459
1709
  if (id === "virtual:clarify-routes" || id === resolveVirtualId("virtual:clarify-routes")) return resolveVirtualId(VIRTUAL_ROUTES);
1460
1710
  if (id === "virtual:clarify-routes/server" || id === resolveVirtualId("virtual:clarify-routes/server")) return resolveVirtualId(VIRTUAL_SERVER_ROUTES);
1461
- if (id === VIRTUAL_OPENAPI_REGISTRY || id === resolveVirtualId(VIRTUAL_OPENAPI_REGISTRY)) return resolveVirtualId(VIRTUAL_OPENAPI_REGISTRY);
1462
- const route = routes.find((r) => r.virtualModuleId === id || r.virtualModuleId === stripVirtualPrefix(id));
1711
+ const moduleId = stripVirtualPrefix(id);
1712
+ if (virtualModules.has(moduleId)) return resolveVirtualId(moduleId);
1713
+ const route = routes.find((r) => r.virtualModuleId === id || r.virtualModuleId === moduleId);
1463
1714
  if (route) return resolveVirtualId(route.virtualModuleId);
1464
1715
  return null;
1465
1716
  },
@@ -1481,10 +1732,11 @@ function clarifyPlugin(options = {}) {
1481
1732
  transformIndexHtml: {
1482
1733
  order: "pre",
1483
1734
  async handler(html, transformCtx) {
1735
+ const clientEntryId = transformCtx.server ? `/@id/${VIRTUAL_CLIENT_ENTRY}` : VIRTUAL_CLIENT_ENTRY;
1484
1736
  const result = await runHooks(clarifyPlugins, "html:transform", {
1485
1737
  html,
1486
1738
  tags: [],
1487
- clientEntryId: transformCtx.server ? `/@id/${VIRTUAL_CLIENT_ENTRY}` : VIRTUAL_CLIENT_ENTRY,
1739
+ clientEntryId,
1488
1740
  dev: Boolean(transformCtx.server)
1489
1741
  }, ctx);
1490
1742
  return {
@@ -1512,7 +1764,7 @@ function clarifyPlugin(options = {}) {
1512
1764
  if (id === "virtual:clarify-config") return id;
1513
1765
  if (id === "virtual:clarify-routes") return id;
1514
1766
  if (id === "virtual:clarify-routes/server") return id;
1515
- if (id === VIRTUAL_OPENAPI_REGISTRY) return id;
1767
+ if (virtualModules.has(stripVirtualPrefix(id))) return stripVirtualPrefix(id);
1516
1768
  if (routes.find((r) => r.virtualModuleId === id)) return id;
1517
1769
  return null;
1518
1770
  },
@@ -1562,5 +1814,11 @@ Object.defineProperty(exports, "loadClarifyConfig", {
1562
1814
  return loadClarifyConfig;
1563
1815
  }
1564
1816
  });
1817
+ Object.defineProperty(exports, "resolveClarifySite", {
1818
+ enumerable: true,
1819
+ get: function() {
1820
+ return resolveClarifySite;
1821
+ }
1822
+ });
1565
1823
 
1566
- //# sourceMappingURL=plugin--jQ6D6O4.cjs.map
1824
+ //# sourceMappingURL=plugin-GTN_SQ7d.cjs.map