boltdocs 2.9.1 → 2.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters-HVuXENAe.mjs +6 -0
- package/dist/adapters-uGz-UKQa.cjs +6 -0
- package/dist/client/index.cjs +136 -125
- package/dist/client/index.js +136 -125
- package/dist/client/mdx.cjs +1 -1
- package/dist/client/mdx.js +1 -1
- package/dist/client/primitives.cjs +1 -1
- package/dist/client/primitives.js +1 -1
- package/dist/config-BCqkPkjC.mjs +7 -0
- package/dist/{config-GPVEdeon.mjs → config-BPWGa2W3.mjs} +1 -1
- package/dist/{config-kIQOJHXC.cjs → config-CnmfjOE4.cjs} +1 -1
- package/dist/config-D7ERJhqe.cjs +7 -0
- package/dist/{csp-RJyh0nHV.mjs → csp-DtM6q2gZ.mjs} +1 -1
- package/dist/{doctor-DzEgMMRo.cjs → doctor-A17UhTRn.cjs} +1 -1
- package/dist/{doctor-D8fl3Sbg.cjs → doctor-BL6c8x2Q.cjs} +1 -1
- package/dist/{doctor-C19DsI1e.mjs → doctor-Bc-Tx_md.mjs} +1 -1
- package/dist/{headers-HD_qTVVW.mjs → headers-DK0Hw7iB.mjs} +1 -1
- package/dist/{image-CjYu9Eml.js → image-BCDyLHyD.js} +1 -4
- package/dist/{image-B9YA_oQ_.cjs → image-DWdmichO.cjs} +1 -4
- package/dist/index-ZqgZUvwp.d.mts +274 -0
- package/dist/index-aWC0KjDF.d.cts +274 -0
- package/dist/{mdx-WXjAMS8Y.cjs → mdx-9QJoSITt.cjs} +1 -1
- package/dist/{mdx-Cy_c2zio.js → mdx-DFbspTP9.js} +1 -1
- package/dist/node/cli-entry.cjs +3 -3
- package/dist/node/cli-entry.mjs +3 -3
- package/dist/node/index.cjs +1 -1
- package/dist/node/index.d.cts +1 -271
- package/dist/node/index.d.mts +2 -273
- package/dist/node/index.mjs +1 -1
- package/dist/node/routes/worker.cjs +1 -1
- package/dist/node/routes/worker.mjs +1 -1
- package/dist/node-JQ6C-CBi.mjs +105 -0
- package/dist/node-cZgna618.cjs +105 -0
- package/dist/{package-ByXh2JHp.cjs → package-BlYktJR3.cjs} +1 -1
- package/dist/{package-DQBAV8uo.mjs → package-Di7alQGe.mjs} +1 -1
- package/dist/{parser-B8naUXl2.cjs → parser-CHsNUEhU.cjs} +1 -1
- package/dist/{parser-IR9tfyGE.mjs → parser-Ww0eOqs7.mjs} +1 -1
- package/dist/plugin-BCCYSeId.cjs +61 -0
- package/dist/plugin-Bu75gPjx.mjs +61 -0
- package/dist/{routes-DX8ryKjs.cjs → routes-B2joDAlC.cjs} +1 -1
- package/dist/{routes-DgTQxpZi.cjs → routes-CyKj3dzf.cjs} +1 -1
- package/dist/{routes-TUSK5wD7.mjs → routes-DMBny9t9.mjs} +1 -1
- package/dist/{routes-DR13Y1em.mjs → routes-DZjS8QSV.mjs} +1 -1
- package/dist/server/index.cjs +6 -0
- package/dist/server/index.d.cts +3 -0
- package/dist/server/index.d.mts +3 -0
- package/dist/server/index.mjs +6 -0
- package/package.json +6 -1
- package/dist/config-BqZUYono.cjs +0 -7
- package/dist/config-DBKBEj3j.mjs +0 -7
- package/dist/node-B7qbjtLl.mjs +0 -105
- package/dist/node-Dj3-2YYP.cjs +0 -105
- package/dist/plugin-BkquLpOI.mjs +0 -61
- package/dist/plugin-CL1LsQXk.cjs +0 -61
- /package/dist/{csp-CBysWlAf.cjs → csp-DbbF3R9Q.cjs} +0 -0
- /package/dist/{generator-B6lrnDZ-.mjs → generator-BaSvGlxR.mjs} +0 -0
- /package/dist/{generator-DtUU3Cfk.cjs → generator-DsajVrDx.cjs} +0 -0
- /package/dist/{headers-BJ4jJAhU.cjs → headers-DuCAOgYX.cjs} +0 -0
- /package/dist/{meta-loader-CxAoQthW.mjs → meta-loader-CD3Yptr5.mjs} +0 -0
- /package/dist/{meta-loader-C9MKjAs0.cjs → meta-loader-CjphwtS7.cjs} +0 -0
- /package/dist/{parser-D4oEyuP-.cjs → parser-CLtGv_XH.cjs} +0 -0
- /package/dist/{worker-pool-m81xuM5v.cjs → worker-pool-CgOMozUz.cjs} +0 -0
- /package/dist/{worker-pool-C6r6V05j.mjs → worker-pool-v6tW2MDQ.mjs} +0 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Boltdocs - https://boltdocs.vercel.app
|
|
3
|
+
* Copyright (c) 2026 Jesus Alcala
|
|
4
|
+
* Licensed under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
import{r as e}from"./errors-C0Hx15Ag.mjs";import{t}from"./types-generator-BRyf4o9F.mjs";import n from"node:path";import r from"node:fs";import{z as i}from"zod";import{warn as a}from"@bdocs/dui";function o(e){return e}const s=i.object({icon:i.string().max(50),link:i.string().url()});i.object({text:i.string().max(2e3).optional()});const c=i.object({name:i.string(),enforce:i.enum([`pre`,`post`]).optional(),version:i.string().optional(),boltdocsVersion:i.string().optional(),remarkPlugins:i.array(i.unknown()).optional(),rehypePlugins:i.array(i.unknown()).optional(),vitePlugins:i.array(i.unknown()).optional(),components:i.record(i.string(),i.string()).optional(),hooks:i.record(i.string(),i.unknown()).optional()}),l=i.object({title:i.union([i.string(),i.record(i.string(),i.string())]).optional(),description:i.union([i.string(),i.record(i.string(),i.string())]).optional(),logo:i.union([i.string(),i.object({dark:i.string(),light:i.string(),alt:i.string().optional(),width:i.number().optional(),height:i.number().optional()})]).optional(),navbar:i.array(i.object({label:i.union([i.string(),i.record(i.string(),i.string())]),href:i.string(),items:i.array(i.object({label:i.union([i.string(),i.record(i.string(),i.string())]),href:i.string()})).optional()})).optional(),sidebar:i.record(i.string(),i.array(i.object({text:i.string(),link:i.string()}))).optional(),sidebarGroups:i.record(i.string(),i.object({title:i.union([i.string(),i.record(i.string(),i.string())]).optional(),icon:i.string().optional()})).optional(),socialLinks:i.array(s).optional(),editLink:i.string().refine(e=>!e||e.includes(`:path`),{message:`editLink must contain ':path' placeholder if specified`}).optional(),communityHelp:i.string().url().optional(),version:i.string().max(50).optional(),githubRepo:i.string().max(100).optional(),favicon:i.string().optional(),tabs:i.array(i.object({id:i.string(),text:i.union([i.string(),i.record(i.string(),i.string())]),icon:i.string().optional()})).optional(),codeTheme:i.union([i.string(),i.object({light:i.string(),dark:i.string()})]).optional()}),u=i.union([i.string(),i.object({rules:i.array(i.object({userAgent:i.string(),allow:i.union([i.string(),i.array(i.string())]).optional(),disallow:i.union([i.string(),i.array(i.string())]).optional()})).optional(),sitemaps:i.array(i.string().url()).optional()})]),d=i.object({defaultLocale:i.string(),locales:i.union([i.record(i.string(),i.string()),i.array(i.string())]).transform(e=>Array.isArray(e)?Object.fromEntries(e.map(e=>[e,e])):e),localeConfigs:i.record(i.string(),i.object({label:i.string().optional(),direction:i.enum([`ltr`,`rtl`]).optional(),htmlLang:i.string().optional(),calendar:i.string().optional()})).optional()}),f=i.object({defaultVersion:i.string(),prefix:i.string().optional(),versions:i.array(i.object({label:i.string(),path:i.string()}))}),p=i.object({headers:i.record(i.string(),i.string()).optional(),enableCSP:i.boolean().optional(),customHeaders:i.record(i.string(),i.string()).optional()}),m=i.object({metatags:i.record(i.string(),i.string()).optional(),indexing:i.enum([`all`,`public`]).optional(),thumbnails:i.object({background:i.string().optional()}).optional()}),h=i.object({measurementId:i.string().min(1,`Measurement ID is required for GA4`),debug:i.boolean().optional(),anonymizeIp:i.boolean().optional(),sendPageView:i.boolean().optional(),cookieFlags:i.string().optional(),autoTrack:i.object({pageViews:i.boolean().optional(),downloads:i.boolean().optional(),externalLinks:i.boolean().optional(),search:i.boolean().optional()}).optional()}),g=i.object({tagId:i.string().min(1,`Tag ID is required for GTM`),dataLayerName:i.string().optional(),preview:i.string().optional()}),_=i.object({appId:i.string().min(1,`Algolia App ID is required`),apiKey:i.string().min(1,`Algolia API Key is required`),indexName:i.string().min(1,`Algolia Index Name is required`)}),v=i.object({repo:i.string().min(1,`Giscus repository is required`),repoId:i.string().min(1,`Giscus repository ID is required`),category:i.string().optional(),categoryId:i.string().optional(),mapping:i.enum([`pathname`,`url`,`title`,`og:title`,`specific`,`number`]).optional(),strict:i.union([i.enum([`0`,`1`]),i.boolean()]).optional(),reactionsEnabled:i.union([i.enum([`0`,`1`]),i.boolean()]).optional(),emitMetadata:i.union([i.enum([`0`,`1`]),i.boolean()]).optional(),inputPosition:i.enum([`top`,`bottom`]).optional(),theme:i.string().optional(),darkTheme:i.string().optional(),lang:i.string().optional(),loading:i.enum([`lazy`,`eager`]).optional()}),y=i.object({enabled:i.boolean(),owner:i.string().min(1,`GitHub owner is required`),repo:i.string().min(1,`GitHub repository name is required`),categorySlug:i.string().optional(),endpoint:i.string().optional()}),b=i.object({ga4:h.optional(),gtm:g.optional(),algolia:_.optional(),feedback:i.object({giscus:v.optional(),custom:y.optional()}).optional()}),x=i.object({siteUrl:i.string().url().optional(),docsDir:i.string().optional(),base:i.string().optional(),theme:l.optional(),i18n:d.optional(),versions:f.optional(),plugins:i.array(c).optional(),robots:u.optional(),security:p.optional(),seo:m.optional(),integrations:b.optional(),vite:i.record(i.string(),i.unknown()).optional()}),S=[`boltdocs.config.js`,`boltdocs.config.mjs`,`boltdocs.config.ts`];async function C(t,i=process.cwd()){let o=i,s={docsDir:n.resolve(t),theme:{title:`Boltdocs`,description:`A Vite documentation framework`,navbar:[{label:`Home`,href:`/`},{label:`Documentation`,href:`/docs`}],codeTheme:{light:`github-light`,dark:`github-dark`}}},c={};for(let e of S){let t=n.resolve(o,e);if(r.existsSync(t))try{let{loadConfigFromFile:e}=await import(`vite`),n=await e({command:`serve`,mode:`development`},t,o);if(n){c=n.config;break}}catch(t){a(`Failed to load config from ${e}:`,t)}}let l={title:c.title,description:c.description,logo:c.logo,favicon:c.favicon,navbar:c.navbar,sidebar:c.sidebar,sidebarGroups:c.theme?.sidebarGroups,socialLinks:c.socialLinks,githubRepo:c.githubRepo,tabs:c.tabs,codeTheme:c.codeTheme,communityHelp:c.communityHelp,version:c.version,editLink:c.editLink,...c.theme||{}},u=Object.fromEntries(Object.entries(l).filter(([e,t])=>t!==void 0));u.navbar&&=u.navbar.map(e=>({label:e.label||e.text||``,href:e.href||e.link||e.to||``,items:e.items}));let d={docsDir:n.resolve(t),theme:{...s.theme,...u},i18n:c.i18n?{...c.i18n,locales:Array.isArray(c.i18n.locales)?Object.fromEntries(c.i18n.locales.map(e=>[e,e])):c.i18n.locales}:void 0,versions:c.versions,siteUrl:c.siteUrl,base:c.base,seo:c.seo,plugins:c.plugins||[],robots:c.robots,security:c.security,integrations:c.integrations,vite:c.vite},f=x.safeParse(d);if(!f.success)throw new e(`Invalid Boltdocs configuration:\n${f.error.issues.map(e=>` - ${e.path.join(`.`)}: ${e.message}`).join(`
|
|
7
|
+
`)}`);return f.data}async function w(e,n=process.cwd()){let r=await C(e,n);return t(r,e,n),r}export{o as a,c as i,C as n,w as r,S as t};
|
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
* Copyright (c) 2026 Jesus Alcala
|
|
4
4
|
* Licensed under the MIT License.
|
|
5
5
|
*/
|
|
6
|
-
import{a as e,n as t,r as n,t as r}from"./config-
|
|
6
|
+
import{a as e,n as t,r as n,t as r}from"./config-BCqkPkjC.mjs";export{r as CONFIG_FILES,e as defineConfig,t as resolveConfig,n as resolveConfigAndGenerateTypes};
|
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
* Copyright (c) 2026 Jesus Alcala
|
|
4
4
|
* Licensed under the MIT License.
|
|
5
5
|
*/
|
|
6
|
-
const e=require(`./config-
|
|
6
|
+
const e=require(`./config-D7ERJhqe.cjs`);exports.CONFIG_FILES=e.t,exports.defineConfig=e.a,exports.resolveConfig=e.n,exports.resolveConfigAndGenerateTypes=e.r;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Boltdocs - https://boltdocs.vercel.app
|
|
3
|
+
* Copyright (c) 2026 Jesus Alcala
|
|
4
|
+
* Licensed under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
const e=require(`./chunk-Ds5LZdWN.cjs`),t=require(`./errors-Dx6JHoKE.cjs`),n=require(`./types-generator-BqNKCbrx.cjs`);let r=require(`node:path`);r=e.n(r);let i=require(`node:fs`);i=e.n(i);let a=require(`zod`),o=require(`@bdocs/dui`);function s(e){return e}const c=a.z.object({icon:a.z.string().max(50),link:a.z.string().url()});a.z.object({text:a.z.string().max(2e3).optional()});const l=a.z.object({name:a.z.string(),enforce:a.z.enum([`pre`,`post`]).optional(),version:a.z.string().optional(),boltdocsVersion:a.z.string().optional(),remarkPlugins:a.z.array(a.z.unknown()).optional(),rehypePlugins:a.z.array(a.z.unknown()).optional(),vitePlugins:a.z.array(a.z.unknown()).optional(),components:a.z.record(a.z.string(),a.z.string()).optional(),hooks:a.z.record(a.z.string(),a.z.unknown()).optional()}),u=a.z.object({title:a.z.union([a.z.string(),a.z.record(a.z.string(),a.z.string())]).optional(),description:a.z.union([a.z.string(),a.z.record(a.z.string(),a.z.string())]).optional(),logo:a.z.union([a.z.string(),a.z.object({dark:a.z.string(),light:a.z.string(),alt:a.z.string().optional(),width:a.z.number().optional(),height:a.z.number().optional()})]).optional(),navbar:a.z.array(a.z.object({label:a.z.union([a.z.string(),a.z.record(a.z.string(),a.z.string())]),href:a.z.string(),items:a.z.array(a.z.object({label:a.z.union([a.z.string(),a.z.record(a.z.string(),a.z.string())]),href:a.z.string()})).optional()})).optional(),sidebar:a.z.record(a.z.string(),a.z.array(a.z.object({text:a.z.string(),link:a.z.string()}))).optional(),sidebarGroups:a.z.record(a.z.string(),a.z.object({title:a.z.union([a.z.string(),a.z.record(a.z.string(),a.z.string())]).optional(),icon:a.z.string().optional()})).optional(),socialLinks:a.z.array(c).optional(),editLink:a.z.string().refine(e=>!e||e.includes(`:path`),{message:`editLink must contain ':path' placeholder if specified`}).optional(),communityHelp:a.z.string().url().optional(),version:a.z.string().max(50).optional(),githubRepo:a.z.string().max(100).optional(),favicon:a.z.string().optional(),tabs:a.z.array(a.z.object({id:a.z.string(),text:a.z.union([a.z.string(),a.z.record(a.z.string(),a.z.string())]),icon:a.z.string().optional()})).optional(),codeTheme:a.z.union([a.z.string(),a.z.object({light:a.z.string(),dark:a.z.string()})]).optional()}),d=a.z.union([a.z.string(),a.z.object({rules:a.z.array(a.z.object({userAgent:a.z.string(),allow:a.z.union([a.z.string(),a.z.array(a.z.string())]).optional(),disallow:a.z.union([a.z.string(),a.z.array(a.z.string())]).optional()})).optional(),sitemaps:a.z.array(a.z.string().url()).optional()})]),f=a.z.object({defaultLocale:a.z.string(),locales:a.z.union([a.z.record(a.z.string(),a.z.string()),a.z.array(a.z.string())]).transform(e=>Array.isArray(e)?Object.fromEntries(e.map(e=>[e,e])):e),localeConfigs:a.z.record(a.z.string(),a.z.object({label:a.z.string().optional(),direction:a.z.enum([`ltr`,`rtl`]).optional(),htmlLang:a.z.string().optional(),calendar:a.z.string().optional()})).optional()}),p=a.z.object({defaultVersion:a.z.string(),prefix:a.z.string().optional(),versions:a.z.array(a.z.object({label:a.z.string(),path:a.z.string()}))}),m=a.z.object({headers:a.z.record(a.z.string(),a.z.string()).optional(),enableCSP:a.z.boolean().optional(),customHeaders:a.z.record(a.z.string(),a.z.string()).optional()}),h=a.z.object({metatags:a.z.record(a.z.string(),a.z.string()).optional(),indexing:a.z.enum([`all`,`public`]).optional(),thumbnails:a.z.object({background:a.z.string().optional()}).optional()}),g=a.z.object({measurementId:a.z.string().min(1,`Measurement ID is required for GA4`),debug:a.z.boolean().optional(),anonymizeIp:a.z.boolean().optional(),sendPageView:a.z.boolean().optional(),cookieFlags:a.z.string().optional(),autoTrack:a.z.object({pageViews:a.z.boolean().optional(),downloads:a.z.boolean().optional(),externalLinks:a.z.boolean().optional(),search:a.z.boolean().optional()}).optional()}),_=a.z.object({tagId:a.z.string().min(1,`Tag ID is required for GTM`),dataLayerName:a.z.string().optional(),preview:a.z.string().optional()}),v=a.z.object({appId:a.z.string().min(1,`Algolia App ID is required`),apiKey:a.z.string().min(1,`Algolia API Key is required`),indexName:a.z.string().min(1,`Algolia Index Name is required`)}),y=a.z.object({repo:a.z.string().min(1,`Giscus repository is required`),repoId:a.z.string().min(1,`Giscus repository ID is required`),category:a.z.string().optional(),categoryId:a.z.string().optional(),mapping:a.z.enum([`pathname`,`url`,`title`,`og:title`,`specific`,`number`]).optional(),strict:a.z.union([a.z.enum([`0`,`1`]),a.z.boolean()]).optional(),reactionsEnabled:a.z.union([a.z.enum([`0`,`1`]),a.z.boolean()]).optional(),emitMetadata:a.z.union([a.z.enum([`0`,`1`]),a.z.boolean()]).optional(),inputPosition:a.z.enum([`top`,`bottom`]).optional(),theme:a.z.string().optional(),darkTheme:a.z.string().optional(),lang:a.z.string().optional(),loading:a.z.enum([`lazy`,`eager`]).optional()}),b=a.z.object({enabled:a.z.boolean(),owner:a.z.string().min(1,`GitHub owner is required`),repo:a.z.string().min(1,`GitHub repository name is required`),categorySlug:a.z.string().optional(),endpoint:a.z.string().optional()}),x=a.z.object({ga4:g.optional(),gtm:_.optional(),algolia:v.optional(),feedback:a.z.object({giscus:y.optional(),custom:b.optional()}).optional()}),S=a.z.object({siteUrl:a.z.string().url().optional(),docsDir:a.z.string().optional(),base:a.z.string().optional(),theme:u.optional(),i18n:f.optional(),versions:p.optional(),plugins:a.z.array(l).optional(),robots:d.optional(),security:m.optional(),seo:h.optional(),integrations:x.optional(),vite:a.z.record(a.z.string(),a.z.unknown()).optional()}),C=[`boltdocs.config.js`,`boltdocs.config.mjs`,`boltdocs.config.ts`];async function w(e,n=process.cwd()){let a=n,s={docsDir:r.default.resolve(e),theme:{title:`Boltdocs`,description:`A Vite documentation framework`,navbar:[{label:`Home`,href:`/`},{label:`Documentation`,href:`/docs`}],codeTheme:{light:`github-light`,dark:`github-dark`}}},c={};for(let e of C){let t=r.default.resolve(a,e);if(i.default.existsSync(t))try{let{loadConfigFromFile:e}=await import(`vite`),n=await e({command:`serve`,mode:`development`},t,a);if(n){c=n.config;break}}catch(t){(0,o.warn)(`Failed to load config from ${e}:`,t)}}let l={title:c.title,description:c.description,logo:c.logo,favicon:c.favicon,navbar:c.navbar,sidebar:c.sidebar,sidebarGroups:c.theme?.sidebarGroups,socialLinks:c.socialLinks,githubRepo:c.githubRepo,tabs:c.tabs,codeTheme:c.codeTheme,communityHelp:c.communityHelp,version:c.version,editLink:c.editLink,...c.theme||{}},u=Object.fromEntries(Object.entries(l).filter(([e,t])=>t!==void 0));u.navbar&&=u.navbar.map(e=>({label:e.label||e.text||``,href:e.href||e.link||e.to||``,items:e.items}));let d={docsDir:r.default.resolve(e),theme:{...s.theme,...u},i18n:c.i18n?{...c.i18n,locales:Array.isArray(c.i18n.locales)?Object.fromEntries(c.i18n.locales.map(e=>[e,e])):c.i18n.locales}:void 0,versions:c.versions,siteUrl:c.siteUrl,base:c.base,seo:c.seo,plugins:c.plugins||[],robots:c.robots,security:c.security,integrations:c.integrations,vite:c.vite},f=S.safeParse(d);if(!f.success)throw new t.r(`Invalid Boltdocs configuration:\n${f.error.issues.map(e=>` - ${e.path.join(`.`)}: ${e.message}`).join(`
|
|
7
|
+
`)}`);return f.data}async function T(e,t=process.cwd()){let r=await w(e,t);return n.t(r,e,t),r}Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return C}});
|
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
* Copyright (c) 2026 Jesus Alcala
|
|
4
4
|
* Licensed under the MIT License.
|
|
5
5
|
*/
|
|
6
|
-
import{
|
|
6
|
+
import{A as e}from"./node-JQ6C-CBi.mjs";var t=e({getCSPHeader:()=>n});function n(e){let t=process.env.NODE_ENV===`development`,n={"default-src":[`'self'`],"script-src":[`'self'`,`'unsafe-inline'`],"style-src":[`'self'`,`'unsafe-inline'`],"img-src":[`'self'`,`data:`,`https:`],"font-src":[`'self'`],"connect-src":[`'self'`]};return t&&(n[`script-src`]=[`'self'`,`'unsafe-eval'`,`'unsafe-inline'`],n[`style-src`]=[`'self'`,`'unsafe-inline'`]),Object.entries(n).map(([e,t])=>`${e} ${t.join(` `)}`).join(`; `)}export{n,t};
|
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
* Copyright (c) 2026 Jesus Alcala
|
|
4
4
|
* Licensed under the MIT License.
|
|
5
5
|
*/
|
|
6
|
-
const e=require(`./chunk-Ds5LZdWN.cjs`),t=require(`./utils-BWy_8ASj.cjs`),n=require(`./config-BqZUYono.cjs`),r=require(`./node/cli-entry.cjs`);let i=require(`node:path`);i=e.n(i);let a=require(`node:fs`);a=e.n(a);let o=require(`@bdocs/dui`),s=require(`fdir`),c=require(`picomatch`);c=e.n(c);let l=require(`fastest-levenshtein`);const u={$schema:`https://boltdocs.vercel.app/schemas/doctor-config.schema.json`,checks:{metadata:{enabled:!0,titleMin:10,titleMax:60,descriptionMin:50,required:[`title`,`description`],optional:[],validateDates:!1},links:{internal:!0,external:!1,timeout:1e4,concurrency:10,ignore:[]},i18n:{enabled:!0},performance:{enabled:!0,budgets:{maxJSBundleSize:`200kb`,maxCSSBundleSize:`30kb`,maxPageHTMLSize:`80kb`,maxImagesKB:500,maxBuildTime:3e4,maxFontCount:3}}},fix:{confirmChanges:!1,backupFiles:!1,backupPath:`.boltdocs/backups`},reporting:{format:`pretty`,outputFile:`.boltdocs/reports/doctor.json`,failOnError:!1,maxWarnings:-1},severity:{missingTranslation:`warning`,brokenLink:`high`,brokenAnchor:`warning`,largeFile:`warning`,orphanedPage:`low`,duplicateTitle:`low`,shortMetadata:`low`,missingMetadata:`warning`,malformedFrontmatter:`high`,invalidFrontmatter:`high`,budgetExceeded:`warning`},exclude:[]};function d(e,t){if(e==null)return t;if(typeof e==`number`)return e;let n=e.toLowerCase().match(/^(\d+(?:\.\d+)?)\s*(b|kb|mb|gb)?$/);if(!n)return t;let r=Number.parseFloat(n[1]),i=n[2]||`b`;return Math.round(r*({b:1,kb:1024,mb:1024*1024,gb:1024*1024*1024}[i]||1))}function f(e,t,n){return e.doctorConfig.severity[t]||n}async function p(e,t){a.default.existsSync(t)||a.default.mkdirSync(t,{recursive:!0});let n=i.default.basename(e),r=new Date().toISOString().replace(/[:.]/g,`-`),o=i.default.join(t,`${n}.${r}.bak`);a.default.copyFileSync(e,o)}const m=new Map;function h(e){let n=m.get(e);if(n)return n;let r=(async()=>{let n=await t.d(e,!1);return{raw:n.raw,data:n.data,content:n.content}})();return m.set(e,r),r}const g=new Map;function _(e){if(g.has(e))return g.get(e);let t=!1;try{t=a.default.existsSync(e)&&a.default.statSync(e).isFile()}catch{t=!1}return g.set(e,t),t}async function v(e){let t=i.default.resolve(e,`doctor.json`);if(a.default.existsSync(t))try{let e=JSON.parse(a.default.readFileSync(t,`utf-8`));return{...u,...e,checks:{...u.checks,...e.checks,metadata:{...u.checks.metadata,...e.checks?.metadata},links:{...u.checks.links,...e.checks?.links},i18n:{...u.checks.i18n,...e.checks?.i18n},performance:{...u.checks.performance,...e.checks?.performance,budgets:{...u.checks.performance?.budgets,...e.checks?.performance?.budgets}}},fix:{...u.fix,...e.fix},reporting:{...u.reporting,...e.reporting},severity:{...u.severity,...e.severity},exclude:[...u.exclude,...e.exclude||[]]}}catch(e){(0,o.warn)(`Failed to parse doctor.json: ${e}`)}return u}async function y(e,n=process.cwd(),r,o){let c=i.default.resolve(n,`.boltdocs`,`generated`);a.default.existsSync(c)||a.default.mkdirSync(c,{recursive:!0});let l=o;l||=await new s.fdir().withFullPaths().filter(e=>e.endsWith(`.md`)||e.endsWith(`.mdx`)).crawl(e).withPromise();let u=r?.base||`/docs`,d=[];for(let n=0;n<l.length;n+=100){let r=l.slice(n,n+100),a=await Promise.all(r.map(async n=>{let r=i.default.isAbsolute(n)?n:i.default.resolve(e,n),a=i.default.relative(e,r),{data:o}=await h(r),s;return s=o.permalink?o.permalink.startsWith(`/`)?o.permalink:`/${o.permalink}`:t.a(a),u===`/`?s:(u.endsWith(`/`)?u:u+`/`)+(s.startsWith(`/`)?s.substring(1):s)}));d.push(...a)}d.includes(u)||d.push(u);let f={routes:Array.from(new Set(d)).sort(),timestamp:Date.now()};return a.default.writeFileSync(i.default.resolve(c,`link-tree.json`),JSON.stringify(f,null,2)),f}function b(e,t){if(e===t)return 1;if(!e||!t)return 0;let n=e.split(`/`).filter(Boolean),r=t.split(`/`).filter(Boolean),i=n[n.length-1]||``,a=r[r.length-1]||``;if(i===a){let e=0;for(let t of n)r.includes(t)&&e++;return e>=2?.99:.9}if(i.length>3&&a.length>3&&(a.startsWith(i)||i.startsWith(a))&&Math.min(i.length,a.length)/Math.max(i.length,a.length)>.5)return .88;let o=1-(0,l.distance)(i,a)/Math.max(i.length,a.length);return o>.8?o*.95:1-(0,l.distance)(e,t)/Math.max(e.length,t.length)}const x=new Map;function S(e,t){if(x.has(e))return x.get(e);let n=e.length,r=``,i=0;for(let a of t){if(a===e)continue;let t=a.length,o=Math.max(n,t);if(o===0)continue;let s=1-Math.abs(n-t)/o;if(s<=.8&&s<=i)continue;let c=b(e,a);c>i&&(i=c,r=a)}let a={bestMatch:r,similarity:i};return x.set(e,a),a}async function C(e){let n=[];if(!e.doctorConfig.checks.metadata.enabled)return n;let{titleMin:r,titleMax:a,descriptionMin:o}=e.doctorConfig.checks.metadata,s=new Map;for(let c of e.files){let l=t.u(i.default.relative(e.docsDir,c));try{let{raw:i,data:u}=await h(c);if(i.trim().startsWith(`---`)&&i.split(`---`).length>=3&&Object.keys(u).length===0){let t=f(e,`malformedFrontmatter`,`high`);t!==`off`&&n.push({file:l,level:t,message:`Malformed frontmatter (YAML parsing failed).`,suggestion:`Check your YAML syntax for indentation or unquoted special characters.`})}let d=t.g.safeParse(u);if(!d.success){let t=f(e,`invalidFrontmatter`,`high`);if(t!==`off`)for(let e of d.error.issues)n.push({file:l,level:t,message:`Invalid frontmatter field "${e.path.join(`.`)}": ${e.message}`,suggestion:`Ensure the field follows the correct type.`})}let p=Array.from(new Set([`title`,...e.doctorConfig.checks.metadata.required]));for(let t of p)if(u[t]===void 0){let r=f(e,`missingMetadata`,`warning`);r!==`off`&&n.push({file:l,level:r,message:`Missing required frontmatter field: "${t}".`,suggestion:`Add the "${t}" field to your frontmatter.`})}if(e.doctorConfig.checks.metadata.validateDates){let t=[`date`,`lastUpdated`,...e.doctorConfig.checks.metadata.optional.filter(e=>e.toLowerCase().includes(`date`))];for(let r of t)if(u[r]&&Number.isNaN(Date.parse(String(u[r])))){let t=f(e,`invalidFrontmatter`,`high`);t!==`off`&&n.push({file:l,level:t,message:`Invalid date format in field "${r}": "${u[r]}".`,suggestion:`Use a valid ISO date format (e.g., YYYY-MM-DD).`})}}if(u.title){let t=String(u.title);if(t.length<r){let i=f(e,`shortMetadata`,`low`);i!==`off`&&n.push({file:l,level:i,message:`Title is too short (${t.length} chars).`,suggestion:`Titles should be at least ${r} characters for better SEO.`})}else if(t.length>a){let r=f(e,`shortMetadata`,`low`);r!==`off`&&n.push({file:l,level:r,message:`Title is too long (${t.length} chars).`,suggestion:`Titles should be under ${a} characters.`})}let i=s.get(t)||[];i.push(l),s.set(t,i)}if(u.description&&String(u.description).length<o){let t=f(e,`shortMetadata`,`low`);t!==`off`&&n.push({file:l,level:t,message:`Description is very short.`,suggestion:`Descriptions should ideally be at least ${o} characters.`})}}catch(t){if(t instanceof Error){let r=f(e,`malformedFrontmatter`,`high`);r!==`off`&&n.push({file:l,level:r,message:`Malformed frontmatter (YAML error): ${t.message}`,suggestion:`Check your YAML syntax for indentation or unquoted special characters.`})}}}for(let[t,r]of s.entries())if(r.length>1){let i=f(e,`duplicateTitle`,`low`);if(i!==`off`)for(let e of r)n.push({file:e,level:i,message:`Duplicate title found: "${t}"`,suggestion:`Ensure each page has a unique title. Also used in: ${r.filter(t=>t!==e).join(`, `)}`})}return n}async function w(e){let n=[],{internal:r,external:s,ignore:c,timeout:l,concurrency:u}=e.doctorConfig.checks.links;if(!r&&!s&&!e.options.checkExternal)return n;let d=/(?:\[.*?\]\((.*?)\))|(?:href=["']([^"']+)["'])/g,p=new Set,g=5e5;for(let o of e.files){let l=t.u(i.default.relative(e.docsDir,o)),{content:u}=await h(o),v=[...(u.length>g?u.slice(0,g):u).replace(/```[\s\S]*?```/g,``).replace(/`[^`\n]*`/g,``).matchAll(d)];for(let t of v){let u=t[1]||t[2],d=!!t[1];if(!u||c.some(e=>u.includes(e)))continue;if(/^https?:\/\//i.test(u)){(s||e.options.checkExternal)&&p.add({url:u,file:o});continue}if(!r||/^(mailto|tel|#)/i.test(u))continue;let h;try{h=decodeURIComponent(u.split(`#`)[0].split(`?`)[0])}catch{h=u.split(`#`)[0].split(`?`)[0]}if(!h)continue;let g=!1,v=``;if(h.startsWith(`/`))if(e.routeIndex.has(h)||e.routeIndexWithSlash.has(h)||e.routeIndexWithoutSlash.has(h))g=!0;else{let t=e.basePrefix+(h.startsWith(`/`)?h:`/`+h);if(e.routeIndex.has(t)||e.routeIndexWithSlash.has(t))g=!1,v=t;else{let t=e.config.base!==`/`&&h.startsWith(e.config.base||`/`)?h.substring((e.config.base||`/`).length):h,n=t.startsWith(`/`)?t.substring(1):t;v=i.default.join(e.docsDir,n),g=[``,`.md`,`.mdx`,`/index.md`,`/index.mdx`].some(e=>_(v+e))}}else v=i.default.resolve(i.default.dirname(o),h),g=[``,`.md`,`.mdx`,`/index.md`,`/index.mdx`].some(e=>_(v+e));if(!g){let{bestMatch:t,similarity:r}=S(h,e.linkTree.routes),i=!1,s=e.basePrefix+(h.startsWith(`/`)?h:`/`+h);(e.routeIndex.has(s)||e.routeIndexWithSlash.has(s))&&(t=s,r=1,i=!0);let c=r>.6||i,p=r>.75&&t!==h||i,g=f(e,`brokenLink`,`high`);g!==`off`&&n.push({file:l,level:g,message:`Broken internal link: "${u}"`,suggestion:c?`Did you mean "${t}"?`:`Ensure the target exists or check for typos.`,fix:p?async()=>{let e=u.includes(`#`)?`#`+u.split(`#`)[1]:``,n=d?`(${u})`:`href="${u}"`,r=d?`(${t}${e})`:`href="${t}${e}"`,i=a.default.readFileSync(o,`utf-8`).replace(n,r);a.default.writeFileSync(o,i),m.delete(o)}:void 0})}}}if(p.size>0){(0,o.info)(o.colors.gray(`Verifying ${p.size} external links...`));let r=new Map;for(let e of p)r.has(e.url)||r.set(e.url,[]),r.get(e.url).push(e.file);let a=async e=>{try{let t=new AbortController,n=setTimeout(()=>t.abort(),l),r=await fetch(e,{method:`HEAD`,signal:t.signal,headers:{"User-Agent":`boltdocs-doctor/1.0`}});return clearTimeout(n),!r.ok&&r.status!==404?{url:e,ok:(await fetch(e,{method:`GET`,signal:t.signal,headers:{"User-Agent":`boltdocs-doctor/1.0`}})).ok}:{url:e,ok:r.ok}}catch(t){return{url:e,ok:!1,error:t.message}}},s=Array.from(r.keys()),c=[];for(let e=0;e<s.length;e+=u){let t=s.slice(e,e+u),n=await Promise.allSettled(t.map(a));c.push(...n.map(e=>e.status===`fulfilled`?e.value:{url:`unknown`,ok:!1,error:e.reason}))}for(let a of c)if(!a.ok){let o=f(e,`brokenLink`,`warning`);if(o!==`off`){let s=r.get(a.url)||[];for(let r of s)n.push({file:t.u(i.default.relative(e.docsDir,r)),level:o,message:`Broken external link: "${a.url}"`,suggestion:`Verify the URL or update it if it's permanently down. Error: ${a.error||`Status >= 400`}`})}}}return n}async function T(e){let n=[];if(!e.doctorConfig.checks.i18n.enabled||!e.config.i18n)return n;let{defaultLocale:r,locales:o}=e.config.i18n,s=Object.keys(o),c=s.filter(e=>e!==r);for(let o of e.files){let l=t.u(i.default.relative(e.docsDir,o)),u=l.split(`/`),d=u[0];if(d===r){let t=u.slice(1).join(`/`);for(let r of c){let s=i.default.join(e.docsDir,r,t);if(!_(s)){let c=f(e,`missingTranslation`,`warning`);c!==`off`&&n.push({file:l,level:c,message:`Missing translation for locale "${r}"`,suggestion:`Create a version at "${r}/${t}".`,fix:async()=>{let e=i.default.dirname(s);a.default.existsSync(e)||a.default.mkdirSync(e,{recursive:!0}),a.default.copyFileSync(o,s)}})}}}else if(s.includes(d)){let t=u.slice(1).join(`/`);if(!_(i.default.join(e.docsDir,r,t))){let i=f(e,`missingTranslation`,`low`);i!==`off`&&n.push({file:l,level:i,message:`Orphaned translation (source missing in "${r}")`,suggestion:`Remove this file or create the source at "${r}/${t}".`,fix:async()=>{a.default.unlinkSync(o)}})}}}return n}async function E(e){let t=[];if(!e.config.theme.sidebar)return t;let n=new Set,r=e.config.theme.sidebar;for(let[i,a]of Object.entries(r))for(let r of a){if(!r.text){let n=f(e,`invalidFrontmatter`,`warning`);n!==`off`&&t.push({file:`boltdocs.config.ts`,level:n,message:`Sidebar item in group "${i}" is missing a label.`,suggestion:`Add a "text" property to the sidebar item.`})}if(r.link&&(n.add(r.link),!e.routeIndex.has(r.link))){let{bestMatch:n,similarity:i}=S(r.link,e.linkTree.routes),a=i>.6,o=f(e,`brokenLink`,`high`);o!==`off`&&t.push({file:`boltdocs.config.ts`,level:o,message:`Broken sidebar link: "${r.link}"`,suggestion:a?`Did you mean "${n}"?`:`Ensure the route exists and is correctly formatted.`})}}for(let r of e.linkTree.routes)if(!(r===`/`||r===``)&&!n.has(r)){let n=f(e,`orphanedPage`,`low`);n!==`off`&&t.push({file:`Sidebar`,level:n,message:`Orphaned page found: "${r}" is not linked in the sidebar.`,suggestion:`Consider adding it to the sidebar for better discoverability.`})}return t}async function D(e){let t=[],n=e.doctorConfig.checks.performance;if(!n?.enabled)return t;let r=i.default.resolve(e.root,`.boltdocs`,`reports`,`performance.json`);if(!a.default.existsSync(r))return t.push({file:`(build)`,level:f(e,`budgetExceeded`,`warning`),message:"Performance metrics not found. Run `boltdocs build` first."}),t;let o;try{o=JSON.parse(a.default.readFileSync(r,`utf-8`))}catch{return t.push({file:`(build)`,level:f(e,`budgetExceeded`,`warning`),message:`Failed to parse performance metrics file.`}),t}let s=n.budgets,c=f(e,`budgetExceeded`,`warning`),l=d(s?.maxJSBundleSize,1/0),u=d(s?.maxCSSBundleSize,1/0),p=d(s?.maxPageHTMLSize,1/0),m=s?.maxImagesKB??1/0,h=s?.maxBuildTime??1/0,g=s?.maxFontCount??1/0;if(l!==1/0&&o.totalJSBundleSize>l){let e=(o.totalJSBundleSize/1024).toFixed(0),n=(l/1024).toFixed(0);t.push({file:`(build)`,level:c,message:`JS bundle size exceeds budget: ${e}kb > ${n}kb`,suggestion:`Code-split large dependencies or lazy-load route components.`})}if(u!==1/0&&o.totalCSSBundleSize>u){let e=(o.totalCSSBundleSize/1024).toFixed(0),n=(u/1024).toFixed(0);t.push({file:`(build)`,level:c,message:`CSS bundle size exceeds budget: ${e}kb > ${n}kb`,suggestion:`Remove unused styles or split CSS by route.`})}if(p!==1/0){for(let e of o.pages)if(e.htmlSize>p){let n=(e.htmlSize/1024).toFixed(0),r=(p/1024).toFixed(0);t.push({file:e.route,level:c,message:`Page HTML size exceeds budget: ${n}kb > ${r}kb`,suggestion:`Reduce the amount of inline content or split into sub-pages.`})}}let _=m*1024;if(m!==1/0&&o.totalImagesSize>_){let e=(o.totalImagesSize/1024).toFixed(0);t.push({file:`(build)`,level:c,message:`Image assets exceed budget: ${e}kb > ${m}kb`,suggestion:`Optimize images with lossy compression or use next-gen formats (webp/avif).`})}if(h!==1/0&&o.buildTime>h){let e=(o.buildTime/1e3).toFixed(1),n=(h/1e3).toFixed(1);t.push({file:`(build)`,level:c,message:`Build time exceeds budget: ${e}s > ${n}s`,suggestion:"Check for large unoptimized assets or increase `concurrency` in SSG options."})}return g!==1/0&&o.fontCount>g&&t.push({file:`(build)`,level:c,message:`Font files exceed budget: ${o.fontCount} > ${g}`,suggestion:`Reduce the number of font families or use variable fonts.`}),t}async function O(e){let t=i.default.resolve(e,`doctor.json`);if(a.default.existsSync(t)){(0,o.warn)(`"doctor.json" already exists at ${e}.`);return}try{a.default.writeFileSync(t,JSON.stringify(u,null,2)),(0,o.success)(`Created "doctor.json" with default configuration.`)}catch(e){(0,o.error)(`Failed to create "doctor.json": ${e}`)}}async function k(e=process.cwd(),t={}){if(t.init){await O(e);return}r.t();try{let r=await v(e),{format:l}=r.reporting,u=performance.now(),d=await n.n(`docs`,e),f=i.default.resolve(e,`docs`);a.default.existsSync(f)||(l===`pretty`&&(0,o.error)(`Docs dir not found at ${f}`),process.exit(1)),l===`pretty`&&console.log((0,o.double)([` ${o.colors.dim(`Docs dir:`)} ${f}`,` ${o.colors.dim(`Reports:`)} ${e}/.boltdocs/reports/`],{title:`✦ DOCTOR — Documentation Health Check`})),l===`pretty`&&(0,o.info)(o.colors.dim(`🔍 Discovering files and routes...`));let m=(0,c.default)(r.exclude||[]),h=await new s.fdir().withFullPaths().filter(e=>{if(!(e.endsWith(`.md`)||e.endsWith(`.mdx`)))return!1;let t=i.default.relative(f,e).replace(/\\/g,`/`),n=t.split(`/`).some(e=>e.startsWith(`_`)&&e!==`_index.md`&&e!==`_index.mdx`);return!m(t)&&!n}).crawl(f).withPromise();for(let e of h)g.set(e,!0);let _=await y(f,e,d,h),b=d.base||`/`,x=b===`/`?``:b.endsWith(`/`)?b.slice(0,-1):b,S={root:e,docsDir:f,config:d,doctorConfig:r,linkTree:_,files:h,options:t,routeIndex:new Set(_.routes),routeIndexWithSlash:new Set(_.routes.map(e=>e.endsWith(`/`)?e:e+`/`)),routeIndexWithoutSlash:new Set(_.routes.map(e=>e.endsWith(`/`)?e.slice(0,-1):e)),basePrefix:x};l===`pretty`&&(0,o.info)(o.colors.dim(`🧪 Running diagnostic checks in parallel...`));let O=[C(S),w(S),T(S),E(S)];t.budget&&O.push(D(S));let[k,A,j,M,...N]=await Promise.all(O),P=t.budget?N[0]:[],F=[...k,...A,...j,...M,...P];if(l===`pretty`){let e=[{label:`Metadata checks ${k.length>0?`— ${k.length} issue${k.length===1?``:`s`}`:`— OK`}`,done:k.length===0},{label:`Link checks ${A.length>0?`— ${A.length} issue${A.length===1?``:`s`}`:`— OK`}`,done:A.length===0},{label:`i18n checks ${j.length>0?`— ${j.length} issue${j.length===1?``:`s`}`:`— OK`}`,done:j.length===0},{label:`Sidebar checks ${M.length>0?`— ${M.length} issue${M.length===1?``:`s`}`:`— OK`}`,done:M.length===0}];t.budget&&e.push({label:`Performance budget ${P.length>0?`— ${P.length} issue${P.length===1?``:`s`}`:`— OK`}`,done:P.length===0}),console.log(`\n${(0,o.tasks)(e)}`)}let I=0;if(t.fix){for(let e of F)if(e.fix){if(S.doctorConfig.fix.confirmChanges&&!await(0,o.confirm)(`Fix issue in "${e.file}": ${e.message}?`))continue;if(S.doctorConfig.fix.backupFiles){let t=i.default.resolve(S.docsDir,e.file);a.default.existsSync(t)&&await p(t,i.default.resolve(S.root,S.doctorConfig.fix.backupPath))}await e.fix(),I++}}let L=((performance.now()-u)/1e3).toFixed(2),R=F.filter(e=>e.level===`high`).length,z=F.filter(e=>e.level===`warning`).length,B=F.filter(e=>e.level===`low`).length,V={summary:{total:F.length,high:R,warning:z,low:B,fixed:I,duration:L},issues:F.map(e=>({...e,fix:void 0}))};if(r.reporting.outputFile){let t=i.default.resolve(e,r.reporting.outputFile);a.default.existsSync(i.default.dirname(t))||a.default.mkdirSync(i.default.dirname(t),{recursive:!0}),a.default.writeFileSync(t,JSON.stringify(V,null,2))}if(l===`json`)console.log(JSON.stringify(V,null,2));else if(l===`pretty`){let e=F.reduce((e,t)=>(e[t.file]||(e[t.file]=[]),e[t.file].push(t),e),{});if(F.length>0){(0,o.dividerLog)();for(let[n,r]of Object.entries(e)){let e=[];for(let n of r){let r=n.level===`high`?`❌`:n.level===`warning`?`⚠️`:`ℹ️`,i=n.level===`high`?o.colors.red:n.level===`warning`?o.colors.yellow:o.colors.blue;e.push(`${r} ${i(n.level.toUpperCase())}: ${n.message}`),n.suggestion&&e.push(` ${o.colors.dim(`💡 ${n.suggestion}`)}`),t.fix&&n.fix&&e.push(` ${o.colors.green(`✅ Fixed automatically`)}`)}console.log(`\n${(0,o.single)(e,{title:`📄 ${n}`})}`)}(0,o.dividerLog)()}if(F.length===0)console.log((0,o.round)([` Everything looks perfect!`,` Your documentation is in great shape.`,``,` ${o.colors.dim(`Scanned ${h.length} file${h.length===1?``:`s`} in ${L}s`)}`],{title:`✨ Documentation Health Check`}));else{let e=[];R>0&&e.push(o.colors.red(`${R} Critical Error${R===1?``:`s`}`)),z>0&&e.push(o.colors.yellow(`${z} Warning${z===1?``:`s`}`)),B>0&&e.push(o.colors.blue(`${B} Improvement${B===1?``:`s`}`));let t=[...(0,o.bullet)(e).split(`
|
|
6
|
+
const e=require(`./chunk-Ds5LZdWN.cjs`),t=require(`./utils-BWy_8ASj.cjs`),n=require(`./config-D7ERJhqe.cjs`),r=require(`./node/cli-entry.cjs`);let i=require(`node:path`);i=e.n(i);let a=require(`node:fs`);a=e.n(a);let o=require(`@bdocs/dui`),s=require(`fdir`),c=require(`picomatch`);c=e.n(c);let l=require(`fastest-levenshtein`);const u={$schema:`https://boltdocs.vercel.app/schemas/doctor-config.schema.json`,checks:{metadata:{enabled:!0,titleMin:10,titleMax:60,descriptionMin:50,required:[`title`,`description`],optional:[],validateDates:!1},links:{internal:!0,external:!1,timeout:1e4,concurrency:10,ignore:[]},i18n:{enabled:!0},performance:{enabled:!0,budgets:{maxJSBundleSize:`200kb`,maxCSSBundleSize:`30kb`,maxPageHTMLSize:`80kb`,maxImagesKB:500,maxBuildTime:3e4,maxFontCount:3}}},fix:{confirmChanges:!1,backupFiles:!1,backupPath:`.boltdocs/backups`},reporting:{format:`pretty`,outputFile:`.boltdocs/reports/doctor.json`,failOnError:!1,maxWarnings:-1},severity:{missingTranslation:`warning`,brokenLink:`high`,brokenAnchor:`warning`,largeFile:`warning`,orphanedPage:`low`,duplicateTitle:`low`,shortMetadata:`low`,missingMetadata:`warning`,malformedFrontmatter:`high`,invalidFrontmatter:`high`,budgetExceeded:`warning`},exclude:[]};function d(e,t){if(e==null)return t;if(typeof e==`number`)return e;let n=e.toLowerCase().match(/^(\d+(?:\.\d+)?)\s*(b|kb|mb|gb)?$/);if(!n)return t;let r=Number.parseFloat(n[1]),i=n[2]||`b`;return Math.round(r*({b:1,kb:1024,mb:1024*1024,gb:1024*1024*1024}[i]||1))}function f(e,t,n){return e.doctorConfig.severity[t]||n}async function p(e,t){a.default.existsSync(t)||a.default.mkdirSync(t,{recursive:!0});let n=i.default.basename(e),r=new Date().toISOString().replace(/[:.]/g,`-`),o=i.default.join(t,`${n}.${r}.bak`);a.default.copyFileSync(e,o)}const m=new Map;function h(e){let n=m.get(e);if(n)return n;let r=(async()=>{let n=await t.d(e,!1);return{raw:n.raw,data:n.data,content:n.content}})();return m.set(e,r),r}const g=new Map;function _(e){if(g.has(e))return g.get(e);let t=!1;try{t=a.default.existsSync(e)&&a.default.statSync(e).isFile()}catch{t=!1}return g.set(e,t),t}async function v(e){let t=i.default.resolve(e,`doctor.json`);if(a.default.existsSync(t))try{let e=JSON.parse(a.default.readFileSync(t,`utf-8`));return{...u,...e,checks:{...u.checks,...e.checks,metadata:{...u.checks.metadata,...e.checks?.metadata},links:{...u.checks.links,...e.checks?.links},i18n:{...u.checks.i18n,...e.checks?.i18n},performance:{...u.checks.performance,...e.checks?.performance,budgets:{...u.checks.performance?.budgets,...e.checks?.performance?.budgets}}},fix:{...u.fix,...e.fix},reporting:{...u.reporting,...e.reporting},severity:{...u.severity,...e.severity},exclude:[...u.exclude,...e.exclude||[]]}}catch(e){(0,o.warn)(`Failed to parse doctor.json: ${e}`)}return u}async function y(e,n=process.cwd(),r,o){let c=i.default.resolve(n,`.boltdocs`,`generated`);a.default.existsSync(c)||a.default.mkdirSync(c,{recursive:!0});let l=o;l||=await new s.fdir().withFullPaths().filter(e=>e.endsWith(`.md`)||e.endsWith(`.mdx`)).crawl(e).withPromise();let u=r?.base||`/docs`,d=[];for(let n=0;n<l.length;n+=100){let r=l.slice(n,n+100),a=await Promise.all(r.map(async n=>{let r=i.default.isAbsolute(n)?n:i.default.resolve(e,n),a=i.default.relative(e,r),{data:o}=await h(r),s;return s=o.permalink?o.permalink.startsWith(`/`)?o.permalink:`/${o.permalink}`:t.a(a),u===`/`?s:(u.endsWith(`/`)?u:u+`/`)+(s.startsWith(`/`)?s.substring(1):s)}));d.push(...a)}d.includes(u)||d.push(u);let f={routes:Array.from(new Set(d)).sort(),timestamp:Date.now()};return a.default.writeFileSync(i.default.resolve(c,`link-tree.json`),JSON.stringify(f,null,2)),f}function b(e,t){if(e===t)return 1;if(!e||!t)return 0;let n=e.split(`/`).filter(Boolean),r=t.split(`/`).filter(Boolean),i=n[n.length-1]||``,a=r[r.length-1]||``;if(i===a){let e=0;for(let t of n)r.includes(t)&&e++;return e>=2?.99:.9}if(i.length>3&&a.length>3&&(a.startsWith(i)||i.startsWith(a))&&Math.min(i.length,a.length)/Math.max(i.length,a.length)>.5)return .88;let o=1-(0,l.distance)(i,a)/Math.max(i.length,a.length);return o>.8?o*.95:1-(0,l.distance)(e,t)/Math.max(e.length,t.length)}const x=new Map;function S(e,t){if(x.has(e))return x.get(e);let n=e.length,r=``,i=0;for(let a of t){if(a===e)continue;let t=a.length,o=Math.max(n,t);if(o===0)continue;let s=1-Math.abs(n-t)/o;if(s<=.8&&s<=i)continue;let c=b(e,a);c>i&&(i=c,r=a)}let a={bestMatch:r,similarity:i};return x.set(e,a),a}async function C(e){let n=[];if(!e.doctorConfig.checks.metadata.enabled)return n;let{titleMin:r,titleMax:a,descriptionMin:o}=e.doctorConfig.checks.metadata,s=new Map;for(let c of e.files){let l=t.u(i.default.relative(e.docsDir,c));try{let{raw:i,data:u}=await h(c);if(i.trim().startsWith(`---`)&&i.split(`---`).length>=3&&Object.keys(u).length===0){let t=f(e,`malformedFrontmatter`,`high`);t!==`off`&&n.push({file:l,level:t,message:`Malformed frontmatter (YAML parsing failed).`,suggestion:`Check your YAML syntax for indentation or unquoted special characters.`})}let d=t.g.safeParse(u);if(!d.success){let t=f(e,`invalidFrontmatter`,`high`);if(t!==`off`)for(let e of d.error.issues)n.push({file:l,level:t,message:`Invalid frontmatter field "${e.path.join(`.`)}": ${e.message}`,suggestion:`Ensure the field follows the correct type.`})}let p=Array.from(new Set([`title`,...e.doctorConfig.checks.metadata.required]));for(let t of p)if(u[t]===void 0){let r=f(e,`missingMetadata`,`warning`);r!==`off`&&n.push({file:l,level:r,message:`Missing required frontmatter field: "${t}".`,suggestion:`Add the "${t}" field to your frontmatter.`})}if(e.doctorConfig.checks.metadata.validateDates){let t=[`date`,`lastUpdated`,...e.doctorConfig.checks.metadata.optional.filter(e=>e.toLowerCase().includes(`date`))];for(let r of t)if(u[r]&&Number.isNaN(Date.parse(String(u[r])))){let t=f(e,`invalidFrontmatter`,`high`);t!==`off`&&n.push({file:l,level:t,message:`Invalid date format in field "${r}": "${u[r]}".`,suggestion:`Use a valid ISO date format (e.g., YYYY-MM-DD).`})}}if(u.title){let t=String(u.title);if(t.length<r){let i=f(e,`shortMetadata`,`low`);i!==`off`&&n.push({file:l,level:i,message:`Title is too short (${t.length} chars).`,suggestion:`Titles should be at least ${r} characters for better SEO.`})}else if(t.length>a){let r=f(e,`shortMetadata`,`low`);r!==`off`&&n.push({file:l,level:r,message:`Title is too long (${t.length} chars).`,suggestion:`Titles should be under ${a} characters.`})}let i=s.get(t)||[];i.push(l),s.set(t,i)}if(u.description&&String(u.description).length<o){let t=f(e,`shortMetadata`,`low`);t!==`off`&&n.push({file:l,level:t,message:`Description is very short.`,suggestion:`Descriptions should ideally be at least ${o} characters.`})}}catch(t){if(t instanceof Error){let r=f(e,`malformedFrontmatter`,`high`);r!==`off`&&n.push({file:l,level:r,message:`Malformed frontmatter (YAML error): ${t.message}`,suggestion:`Check your YAML syntax for indentation or unquoted special characters.`})}}}for(let[t,r]of s.entries())if(r.length>1){let i=f(e,`duplicateTitle`,`low`);if(i!==`off`)for(let e of r)n.push({file:e,level:i,message:`Duplicate title found: "${t}"`,suggestion:`Ensure each page has a unique title. Also used in: ${r.filter(t=>t!==e).join(`, `)}`})}return n}async function w(e){let n=[],{internal:r,external:s,ignore:c,timeout:l,concurrency:u}=e.doctorConfig.checks.links;if(!r&&!s&&!e.options.checkExternal)return n;let d=/(?:\[.*?\]\((.*?)\))|(?:href=["']([^"']+)["'])/g,p=new Set,g=5e5;for(let o of e.files){let l=t.u(i.default.relative(e.docsDir,o)),{content:u}=await h(o),v=[...(u.length>g?u.slice(0,g):u).replace(/```[\s\S]*?```/g,``).replace(/`[^`\n]*`/g,``).matchAll(d)];for(let t of v){let u=t[1]||t[2],d=!!t[1];if(!u||c.some(e=>u.includes(e)))continue;if(/^https?:\/\//i.test(u)){(s||e.options.checkExternal)&&p.add({url:u,file:o});continue}if(!r||/^(mailto|tel|#)/i.test(u))continue;let h;try{h=decodeURIComponent(u.split(`#`)[0].split(`?`)[0])}catch{h=u.split(`#`)[0].split(`?`)[0]}if(!h)continue;let g=!1,v=``;if(h.startsWith(`/`))if(e.routeIndex.has(h)||e.routeIndexWithSlash.has(h)||e.routeIndexWithoutSlash.has(h))g=!0;else{let t=e.basePrefix+(h.startsWith(`/`)?h:`/`+h);if(e.routeIndex.has(t)||e.routeIndexWithSlash.has(t))g=!1,v=t;else{let t=e.config.base!==`/`&&h.startsWith(e.config.base||`/`)?h.substring((e.config.base||`/`).length):h,n=t.startsWith(`/`)?t.substring(1):t;v=i.default.join(e.docsDir,n),g=[``,`.md`,`.mdx`,`/index.md`,`/index.mdx`].some(e=>_(v+e))}}else v=i.default.resolve(i.default.dirname(o),h),g=[``,`.md`,`.mdx`,`/index.md`,`/index.mdx`].some(e=>_(v+e));if(!g){let{bestMatch:t,similarity:r}=S(h,e.linkTree.routes),i=!1,s=e.basePrefix+(h.startsWith(`/`)?h:`/`+h);(e.routeIndex.has(s)||e.routeIndexWithSlash.has(s))&&(t=s,r=1,i=!0);let c=r>.6||i,p=r>.75&&t!==h||i,g=f(e,`brokenLink`,`high`);g!==`off`&&n.push({file:l,level:g,message:`Broken internal link: "${u}"`,suggestion:c?`Did you mean "${t}"?`:`Ensure the target exists or check for typos.`,fix:p?async()=>{let e=u.includes(`#`)?`#`+u.split(`#`)[1]:``,n=d?`(${u})`:`href="${u}"`,r=d?`(${t}${e})`:`href="${t}${e}"`,i=a.default.readFileSync(o,`utf-8`).replace(n,r);a.default.writeFileSync(o,i),m.delete(o)}:void 0})}}}if(p.size>0){(0,o.info)(o.colors.gray(`Verifying ${p.size} external links...`));let r=new Map;for(let e of p)r.has(e.url)||r.set(e.url,[]),r.get(e.url).push(e.file);let a=async e=>{try{let t=new AbortController,n=setTimeout(()=>t.abort(),l),r=await fetch(e,{method:`HEAD`,signal:t.signal,headers:{"User-Agent":`boltdocs-doctor/1.0`}});return clearTimeout(n),!r.ok&&r.status!==404?{url:e,ok:(await fetch(e,{method:`GET`,signal:t.signal,headers:{"User-Agent":`boltdocs-doctor/1.0`}})).ok}:{url:e,ok:r.ok}}catch(t){return{url:e,ok:!1,error:t.message}}},s=Array.from(r.keys()),c=[];for(let e=0;e<s.length;e+=u){let t=s.slice(e,e+u),n=await Promise.allSettled(t.map(a));c.push(...n.map(e=>e.status===`fulfilled`?e.value:{url:`unknown`,ok:!1,error:e.reason}))}for(let a of c)if(!a.ok){let o=f(e,`brokenLink`,`warning`);if(o!==`off`){let s=r.get(a.url)||[];for(let r of s)n.push({file:t.u(i.default.relative(e.docsDir,r)),level:o,message:`Broken external link: "${a.url}"`,suggestion:`Verify the URL or update it if it's permanently down. Error: ${a.error||`Status >= 400`}`})}}}return n}async function T(e){let n=[];if(!e.doctorConfig.checks.i18n.enabled||!e.config.i18n)return n;let{defaultLocale:r,locales:o}=e.config.i18n,s=Object.keys(o),c=s.filter(e=>e!==r);for(let o of e.files){let l=t.u(i.default.relative(e.docsDir,o)),u=l.split(`/`),d=u[0];if(d===r){let t=u.slice(1).join(`/`);for(let r of c){let s=i.default.join(e.docsDir,r,t);if(!_(s)){let c=f(e,`missingTranslation`,`warning`);c!==`off`&&n.push({file:l,level:c,message:`Missing translation for locale "${r}"`,suggestion:`Create a version at "${r}/${t}".`,fix:async()=>{let e=i.default.dirname(s);a.default.existsSync(e)||a.default.mkdirSync(e,{recursive:!0}),a.default.copyFileSync(o,s)}})}}}else if(s.includes(d)){let t=u.slice(1).join(`/`);if(!_(i.default.join(e.docsDir,r,t))){let i=f(e,`missingTranslation`,`low`);i!==`off`&&n.push({file:l,level:i,message:`Orphaned translation (source missing in "${r}")`,suggestion:`Remove this file or create the source at "${r}/${t}".`,fix:async()=>{a.default.unlinkSync(o)}})}}}return n}async function E(e){let t=[];if(!e.config.theme.sidebar)return t;let n=new Set,r=e.config.theme.sidebar;for(let[i,a]of Object.entries(r))for(let r of a){if(!r.text){let n=f(e,`invalidFrontmatter`,`warning`);n!==`off`&&t.push({file:`boltdocs.config.ts`,level:n,message:`Sidebar item in group "${i}" is missing a label.`,suggestion:`Add a "text" property to the sidebar item.`})}if(r.link&&(n.add(r.link),!e.routeIndex.has(r.link))){let{bestMatch:n,similarity:i}=S(r.link,e.linkTree.routes),a=i>.6,o=f(e,`brokenLink`,`high`);o!==`off`&&t.push({file:`boltdocs.config.ts`,level:o,message:`Broken sidebar link: "${r.link}"`,suggestion:a?`Did you mean "${n}"?`:`Ensure the route exists and is correctly formatted.`})}}for(let r of e.linkTree.routes)if(!(r===`/`||r===``)&&!n.has(r)){let n=f(e,`orphanedPage`,`low`);n!==`off`&&t.push({file:`Sidebar`,level:n,message:`Orphaned page found: "${r}" is not linked in the sidebar.`,suggestion:`Consider adding it to the sidebar for better discoverability.`})}return t}async function D(e){let t=[],n=e.doctorConfig.checks.performance;if(!n?.enabled)return t;let r=i.default.resolve(e.root,`.boltdocs`,`reports`,`performance.json`);if(!a.default.existsSync(r))return t.push({file:`(build)`,level:f(e,`budgetExceeded`,`warning`),message:"Performance metrics not found. Run `boltdocs build` first."}),t;let o;try{o=JSON.parse(a.default.readFileSync(r,`utf-8`))}catch{return t.push({file:`(build)`,level:f(e,`budgetExceeded`,`warning`),message:`Failed to parse performance metrics file.`}),t}let s=n.budgets,c=f(e,`budgetExceeded`,`warning`),l=d(s?.maxJSBundleSize,1/0),u=d(s?.maxCSSBundleSize,1/0),p=d(s?.maxPageHTMLSize,1/0),m=s?.maxImagesKB??1/0,h=s?.maxBuildTime??1/0,g=s?.maxFontCount??1/0;if(l!==1/0&&o.totalJSBundleSize>l){let e=(o.totalJSBundleSize/1024).toFixed(0),n=(l/1024).toFixed(0);t.push({file:`(build)`,level:c,message:`JS bundle size exceeds budget: ${e}kb > ${n}kb`,suggestion:`Code-split large dependencies or lazy-load route components.`})}if(u!==1/0&&o.totalCSSBundleSize>u){let e=(o.totalCSSBundleSize/1024).toFixed(0),n=(u/1024).toFixed(0);t.push({file:`(build)`,level:c,message:`CSS bundle size exceeds budget: ${e}kb > ${n}kb`,suggestion:`Remove unused styles or split CSS by route.`})}if(p!==1/0){for(let e of o.pages)if(e.htmlSize>p){let n=(e.htmlSize/1024).toFixed(0),r=(p/1024).toFixed(0);t.push({file:e.route,level:c,message:`Page HTML size exceeds budget: ${n}kb > ${r}kb`,suggestion:`Reduce the amount of inline content or split into sub-pages.`})}}let _=m*1024;if(m!==1/0&&o.totalImagesSize>_){let e=(o.totalImagesSize/1024).toFixed(0);t.push({file:`(build)`,level:c,message:`Image assets exceed budget: ${e}kb > ${m}kb`,suggestion:`Optimize images with lossy compression or use next-gen formats (webp/avif).`})}if(h!==1/0&&o.buildTime>h){let e=(o.buildTime/1e3).toFixed(1),n=(h/1e3).toFixed(1);t.push({file:`(build)`,level:c,message:`Build time exceeds budget: ${e}s > ${n}s`,suggestion:"Check for large unoptimized assets or increase `concurrency` in SSG options."})}return g!==1/0&&o.fontCount>g&&t.push({file:`(build)`,level:c,message:`Font files exceed budget: ${o.fontCount} > ${g}`,suggestion:`Reduce the number of font families or use variable fonts.`}),t}async function O(e){let t=i.default.resolve(e,`doctor.json`);if(a.default.existsSync(t)){(0,o.warn)(`"doctor.json" already exists at ${e}.`);return}try{a.default.writeFileSync(t,JSON.stringify(u,null,2)),(0,o.success)(`Created "doctor.json" with default configuration.`)}catch(e){(0,o.error)(`Failed to create "doctor.json": ${e}`)}}async function k(e=process.cwd(),t={}){if(t.init){await O(e);return}r.t();try{let r=await v(e),{format:l}=r.reporting,u=performance.now(),d=await n.n(`docs`,e),f=i.default.resolve(e,`docs`);a.default.existsSync(f)||(l===`pretty`&&(0,o.error)(`Docs dir not found at ${f}`),process.exit(1)),l===`pretty`&&console.log((0,o.double)([` ${o.colors.dim(`Docs dir:`)} ${f}`,` ${o.colors.dim(`Reports:`)} ${e}/.boltdocs/reports/`],{title:`✦ DOCTOR — Documentation Health Check`})),l===`pretty`&&(0,o.info)(o.colors.dim(`🔍 Discovering files and routes...`));let m=(0,c.default)(r.exclude||[]),h=await new s.fdir().withFullPaths().filter(e=>{if(!(e.endsWith(`.md`)||e.endsWith(`.mdx`)))return!1;let t=i.default.relative(f,e).replace(/\\/g,`/`),n=t.split(`/`).some(e=>e.startsWith(`_`)&&e!==`_index.md`&&e!==`_index.mdx`);return!m(t)&&!n}).crawl(f).withPromise();for(let e of h)g.set(e,!0);let _=await y(f,e,d,h),b=d.base||`/`,x=b===`/`?``:b.endsWith(`/`)?b.slice(0,-1):b,S={root:e,docsDir:f,config:d,doctorConfig:r,linkTree:_,files:h,options:t,routeIndex:new Set(_.routes),routeIndexWithSlash:new Set(_.routes.map(e=>e.endsWith(`/`)?e:e+`/`)),routeIndexWithoutSlash:new Set(_.routes.map(e=>e.endsWith(`/`)?e.slice(0,-1):e)),basePrefix:x};l===`pretty`&&(0,o.info)(o.colors.dim(`🧪 Running diagnostic checks in parallel...`));let O=[C(S),w(S),T(S),E(S)];t.budget&&O.push(D(S));let[k,A,j,M,...N]=await Promise.all(O),P=t.budget?N[0]:[],F=[...k,...A,...j,...M,...P];if(l===`pretty`){let e=[{label:`Metadata checks ${k.length>0?`— ${k.length} issue${k.length===1?``:`s`}`:`— OK`}`,done:k.length===0},{label:`Link checks ${A.length>0?`— ${A.length} issue${A.length===1?``:`s`}`:`— OK`}`,done:A.length===0},{label:`i18n checks ${j.length>0?`— ${j.length} issue${j.length===1?``:`s`}`:`— OK`}`,done:j.length===0},{label:`Sidebar checks ${M.length>0?`— ${M.length} issue${M.length===1?``:`s`}`:`— OK`}`,done:M.length===0}];t.budget&&e.push({label:`Performance budget ${P.length>0?`— ${P.length} issue${P.length===1?``:`s`}`:`— OK`}`,done:P.length===0}),console.log(`\n${(0,o.tasks)(e)}`)}let I=0;if(t.fix){for(let e of F)if(e.fix){if(S.doctorConfig.fix.confirmChanges&&!await(0,o.confirm)(`Fix issue in "${e.file}": ${e.message}?`))continue;if(S.doctorConfig.fix.backupFiles){let t=i.default.resolve(S.docsDir,e.file);a.default.existsSync(t)&&await p(t,i.default.resolve(S.root,S.doctorConfig.fix.backupPath))}await e.fix(),I++}}let L=((performance.now()-u)/1e3).toFixed(2),R=F.filter(e=>e.level===`high`).length,z=F.filter(e=>e.level===`warning`).length,B=F.filter(e=>e.level===`low`).length,V={summary:{total:F.length,high:R,warning:z,low:B,fixed:I,duration:L},issues:F.map(e=>({...e,fix:void 0}))};if(r.reporting.outputFile){let t=i.default.resolve(e,r.reporting.outputFile);a.default.existsSync(i.default.dirname(t))||a.default.mkdirSync(i.default.dirname(t),{recursive:!0}),a.default.writeFileSync(t,JSON.stringify(V,null,2))}if(l===`json`)console.log(JSON.stringify(V,null,2));else if(l===`pretty`){let e=F.reduce((e,t)=>(e[t.file]||(e[t.file]=[]),e[t.file].push(t),e),{});if(F.length>0){(0,o.dividerLog)();for(let[n,r]of Object.entries(e)){let e=[];for(let n of r){let r=n.level===`high`?`❌`:n.level===`warning`?`⚠️`:`ℹ️`,i=n.level===`high`?o.colors.red:n.level===`warning`?o.colors.yellow:o.colors.blue;e.push(`${r} ${i(n.level.toUpperCase())}: ${n.message}`),n.suggestion&&e.push(` ${o.colors.dim(`💡 ${n.suggestion}`)}`),t.fix&&n.fix&&e.push(` ${o.colors.green(`✅ Fixed automatically`)}`)}console.log(`\n${(0,o.single)(e,{title:`📄 ${n}`})}`)}(0,o.dividerLog)()}if(F.length===0)console.log((0,o.round)([` Everything looks perfect!`,` Your documentation is in great shape.`,``,` ${o.colors.dim(`Scanned ${h.length} file${h.length===1?``:`s`} in ${L}s`)}`],{title:`✨ Documentation Health Check`}));else{let e=[];R>0&&e.push(o.colors.red(`${R} Critical Error${R===1?``:`s`}`)),z>0&&e.push(o.colors.yellow(`${z} Warning${z===1?``:`s`}`)),B>0&&e.push(o.colors.blue(`${B} Improvement${B===1?``:`s`}`));let t=[...(0,o.bullet)(e).split(`
|
|
7
7
|
`).map(e=>e.trimStart()),``,o.colors.dim(`Scanned ${h.length} file${h.length===1?``:`s`} in ${L}s`)];console.log(`\n${(0,o.double)(t,{title:`Diagnosis Results`})}\n`),I>0&&(0,o.success)(`Successfully fixed ${I} issues automatically!`),R>0?(0,o.error)(`Please fix the critical errors before building for production.`):(0,o.success)(`No critical issues found. You are ready to go!`)}}r.reporting.failOnError&&R>0&&process.exit(1),r.reporting.maxWarnings!==-1&&z>r.reporting.maxWarnings&&(l===`pretty`&&(0,o.error)(`Failed: Too many warnings (${z} > ${r.reporting.maxWarnings})`),process.exit(1))}catch(e){(0,o.error)(`Doctor failed: ${e}`),process.exit(1)}}Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return v}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return O}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return D}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return E}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return k}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return u}});
|
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
* Copyright (c) 2026 Jesus Alcala
|
|
4
4
|
* Licensed under the MIT License.
|
|
5
5
|
*/
|
|
6
|
-
import{P as e}from"./node-B7qbjtLl.mjs";import{a as t,d as n,g as r,u as i}from"./utils-DexBrwiP.mjs";import{n as a}from"./config-DBKBEj3j.mjs";import{t as o}from"./node/cli-entry.mjs";import s from"node:path";import c from"node:fs";import{bullet as l,colors as u,confirm as d,dividerLog as f,double as p,error as m,info as h,round as g,single as _,success as v,tasks as y,warn as b}from"@bdocs/dui";import{fdir as x}from"fdir";import S from"picomatch";import{distance as C}from"fastest-levenshtein";const w={$schema:`https://boltdocs.vercel.app/schemas/doctor-config.schema.json`,checks:{metadata:{enabled:!0,titleMin:10,titleMax:60,descriptionMin:50,required:[`title`,`description`],optional:[],validateDates:!1},links:{internal:!0,external:!1,timeout:1e4,concurrency:10,ignore:[]},i18n:{enabled:!0},performance:{enabled:!0,budgets:{maxJSBundleSize:`200kb`,maxCSSBundleSize:`30kb`,maxPageHTMLSize:`80kb`,maxImagesKB:500,maxBuildTime:3e4,maxFontCount:3}}},fix:{confirmChanges:!1,backupFiles:!1,backupPath:`.boltdocs/backups`},reporting:{format:`pretty`,outputFile:`.boltdocs/reports/doctor.json`,failOnError:!1,maxWarnings:-1},severity:{missingTranslation:`warning`,brokenLink:`high`,brokenAnchor:`warning`,largeFile:`warning`,orphanedPage:`low`,duplicateTitle:`low`,shortMetadata:`low`,missingMetadata:`warning`,malformedFrontmatter:`high`,invalidFrontmatter:`high`,budgetExceeded:`warning`},exclude:[]};function T(e,t){if(e==null)return t;if(typeof e==`number`)return e;let n=e.toLowerCase().match(/^(\d+(?:\.\d+)?)\s*(b|kb|mb|gb)?$/);if(!n)return t;let r=Number.parseFloat(n[1]),i=n[2]||`b`;return Math.round(r*({b:1,kb:1024,mb:1024*1024,gb:1024*1024*1024}[i]||1))}function E(e,t,n){return e.doctorConfig.severity[t]||n}async function D(e,t){c.existsSync(t)||c.mkdirSync(t,{recursive:!0});let n=s.basename(e),r=new Date().toISOString().replace(/[:.]/g,`-`),i=s.join(t,`${n}.${r}.bak`);c.copyFileSync(e,i)}const O=new Map;function k(e){let t=O.get(e);if(t)return t;let r=(async()=>{let t=await n(e,!1);return{raw:t.raw,data:t.data,content:t.content}})();return O.set(e,r),r}const A=new Map;function j(e){if(A.has(e))return A.get(e);let t=!1;try{t=c.existsSync(e)&&c.statSync(e).isFile()}catch{t=!1}return A.set(e,t),t}async function M(e){let t=s.resolve(e,`doctor.json`);if(c.existsSync(t))try{let e=JSON.parse(c.readFileSync(t,`utf-8`));return{...w,...e,checks:{...w.checks,...e.checks,metadata:{...w.checks.metadata,...e.checks?.metadata},links:{...w.checks.links,...e.checks?.links},i18n:{...w.checks.i18n,...e.checks?.i18n},performance:{...w.checks.performance,...e.checks?.performance,budgets:{...w.checks.performance?.budgets,...e.checks?.performance?.budgets}}},fix:{...w.fix,...e.fix},reporting:{...w.reporting,...e.reporting},severity:{...w.severity,...e.severity},exclude:[...w.exclude,...e.exclude||[]]}}catch(e){b(`Failed to parse doctor.json: ${e}`)}return w}async function N(e,n=process.cwd(),r,i){let a=s.resolve(n,`.boltdocs`,`generated`);c.existsSync(a)||c.mkdirSync(a,{recursive:!0});let o=i;o||=await new x().withFullPaths().filter(e=>e.endsWith(`.md`)||e.endsWith(`.mdx`)).crawl(e).withPromise();let l=r?.base||`/docs`,u=[];for(let n=0;n<o.length;n+=100){let r=o.slice(n,n+100),i=await Promise.all(r.map(async n=>{let r=s.isAbsolute(n)?n:s.resolve(e,n),i=s.relative(e,r),{data:a}=await k(r),o;return o=a.permalink?a.permalink.startsWith(`/`)?a.permalink:`/${a.permalink}`:t(i),l===`/`?o:(l.endsWith(`/`)?l:l+`/`)+(o.startsWith(`/`)?o.substring(1):o)}));u.push(...i)}u.includes(l)||u.push(l);let d={routes:Array.from(new Set(u)).sort(),timestamp:Date.now()};return c.writeFileSync(s.resolve(a,`link-tree.json`),JSON.stringify(d,null,2)),d}function P(e,t){if(e===t)return 1;if(!e||!t)return 0;let n=e.split(`/`).filter(Boolean),r=t.split(`/`).filter(Boolean),i=n[n.length-1]||``,a=r[r.length-1]||``;if(i===a){let e=0;for(let t of n)r.includes(t)&&e++;return e>=2?.99:.9}if(i.length>3&&a.length>3&&(a.startsWith(i)||i.startsWith(a))&&Math.min(i.length,a.length)/Math.max(i.length,a.length)>.5)return .88;let o=1-C(i,a)/Math.max(i.length,a.length);return o>.8?o*.95:1-C(e,t)/Math.max(e.length,t.length)}const F=new Map;function I(e,t){if(F.has(e))return F.get(e);let n=e.length,r=``,i=0;for(let a of t){if(a===e)continue;let t=a.length,o=Math.max(n,t);if(o===0)continue;let s=1-Math.abs(n-t)/o;if(s<=.8&&s<=i)continue;let c=P(e,a);c>i&&(i=c,r=a)}let a={bestMatch:r,similarity:i};return F.set(e,a),a}async function L(e){let t=[];if(!e.doctorConfig.checks.metadata.enabled)return t;let{titleMin:n,titleMax:a,descriptionMin:o}=e.doctorConfig.checks.metadata,c=new Map;for(let l of e.files){let u=i(s.relative(e.docsDir,l));try{let{raw:i,data:s}=await k(l);if(i.trim().startsWith(`---`)&&i.split(`---`).length>=3&&Object.keys(s).length===0){let n=E(e,`malformedFrontmatter`,`high`);n!==`off`&&t.push({file:u,level:n,message:`Malformed frontmatter (YAML parsing failed).`,suggestion:`Check your YAML syntax for indentation or unquoted special characters.`})}let d=r.safeParse(s);if(!d.success){let n=E(e,`invalidFrontmatter`,`high`);if(n!==`off`)for(let e of d.error.issues)t.push({file:u,level:n,message:`Invalid frontmatter field "${e.path.join(`.`)}": ${e.message}`,suggestion:`Ensure the field follows the correct type.`})}let f=Array.from(new Set([`title`,...e.doctorConfig.checks.metadata.required]));for(let n of f)if(s[n]===void 0){let r=E(e,`missingMetadata`,`warning`);r!==`off`&&t.push({file:u,level:r,message:`Missing required frontmatter field: "${n}".`,suggestion:`Add the "${n}" field to your frontmatter.`})}if(e.doctorConfig.checks.metadata.validateDates){let n=[`date`,`lastUpdated`,...e.doctorConfig.checks.metadata.optional.filter(e=>e.toLowerCase().includes(`date`))];for(let r of n)if(s[r]&&Number.isNaN(Date.parse(String(s[r])))){let n=E(e,`invalidFrontmatter`,`high`);n!==`off`&&t.push({file:u,level:n,message:`Invalid date format in field "${r}": "${s[r]}".`,suggestion:`Use a valid ISO date format (e.g., YYYY-MM-DD).`})}}if(s.title){let r=String(s.title);if(r.length<n){let i=E(e,`shortMetadata`,`low`);i!==`off`&&t.push({file:u,level:i,message:`Title is too short (${r.length} chars).`,suggestion:`Titles should be at least ${n} characters for better SEO.`})}else if(r.length>a){let n=E(e,`shortMetadata`,`low`);n!==`off`&&t.push({file:u,level:n,message:`Title is too long (${r.length} chars).`,suggestion:`Titles should be under ${a} characters.`})}let i=c.get(r)||[];i.push(u),c.set(r,i)}if(s.description&&String(s.description).length<o){let n=E(e,`shortMetadata`,`low`);n!==`off`&&t.push({file:u,level:n,message:`Description is very short.`,suggestion:`Descriptions should ideally be at least ${o} characters.`})}}catch(n){if(n instanceof Error){let r=E(e,`malformedFrontmatter`,`high`);r!==`off`&&t.push({file:u,level:r,message:`Malformed frontmatter (YAML error): ${n.message}`,suggestion:`Check your YAML syntax for indentation or unquoted special characters.`})}}}for(let[n,r]of c.entries())if(r.length>1){let i=E(e,`duplicateTitle`,`low`);if(i!==`off`)for(let e of r)t.push({file:e,level:i,message:`Duplicate title found: "${n}"`,suggestion:`Ensure each page has a unique title. Also used in: ${r.filter(t=>t!==e).join(`, `)}`})}return t}async function R(e){let t=[],{internal:n,external:r,ignore:a,timeout:o,concurrency:l}=e.doctorConfig.checks.links;if(!n&&!r&&!e.options.checkExternal)return t;let d=/(?:\[.*?\]\((.*?)\))|(?:href=["']([^"']+)["'])/g,f=new Set,p=5e5;for(let o of e.files){let l=i(s.relative(e.docsDir,o)),{content:u}=await k(o),m=[...(u.length>p?u.slice(0,p):u).replace(/```[\s\S]*?```/g,``).replace(/`[^`\n]*`/g,``).matchAll(d)];for(let i of m){let u=i[1]||i[2],d=!!i[1];if(!u||a.some(e=>u.includes(e)))continue;if(/^https?:\/\//i.test(u)){(r||e.options.checkExternal)&&f.add({url:u,file:o});continue}if(!n||/^(mailto|tel|#)/i.test(u))continue;let p;try{p=decodeURIComponent(u.split(`#`)[0].split(`?`)[0])}catch{p=u.split(`#`)[0].split(`?`)[0]}if(!p)continue;let m=!1,h=``;if(p.startsWith(`/`))if(e.routeIndex.has(p)||e.routeIndexWithSlash.has(p)||e.routeIndexWithoutSlash.has(p))m=!0;else{let t=e.basePrefix+(p.startsWith(`/`)?p:`/`+p);if(e.routeIndex.has(t)||e.routeIndexWithSlash.has(t))m=!1,h=t;else{let t=e.config.base!==`/`&&p.startsWith(e.config.base||`/`)?p.substring((e.config.base||`/`).length):p,n=t.startsWith(`/`)?t.substring(1):t;h=s.join(e.docsDir,n),m=[``,`.md`,`.mdx`,`/index.md`,`/index.mdx`].some(e=>j(h+e))}}else h=s.resolve(s.dirname(o),p),m=[``,`.md`,`.mdx`,`/index.md`,`/index.mdx`].some(e=>j(h+e));if(!m){let{bestMatch:n,similarity:r}=I(p,e.linkTree.routes),i=!1,a=e.basePrefix+(p.startsWith(`/`)?p:`/`+p);(e.routeIndex.has(a)||e.routeIndexWithSlash.has(a))&&(n=a,r=1,i=!0);let s=r>.6||i,f=r>.75&&n!==p||i,m=E(e,`brokenLink`,`high`);m!==`off`&&t.push({file:l,level:m,message:`Broken internal link: "${u}"`,suggestion:s?`Did you mean "${n}"?`:`Ensure the target exists or check for typos.`,fix:f?async()=>{let e=u.includes(`#`)?`#`+u.split(`#`)[1]:``,t=d?`(${u})`:`href="${u}"`,r=d?`(${n}${e})`:`href="${n}${e}"`,i=c.readFileSync(o,`utf-8`).replace(t,r);c.writeFileSync(o,i),O.delete(o)}:void 0})}}}if(f.size>0){h(u.gray(`Verifying ${f.size} external links...`));let n=new Map;for(let e of f)n.has(e.url)||n.set(e.url,[]),n.get(e.url).push(e.file);let r=async e=>{try{let t=new AbortController,n=setTimeout(()=>t.abort(),o),r=await fetch(e,{method:`HEAD`,signal:t.signal,headers:{"User-Agent":`boltdocs-doctor/1.0`}});return clearTimeout(n),!r.ok&&r.status!==404?{url:e,ok:(await fetch(e,{method:`GET`,signal:t.signal,headers:{"User-Agent":`boltdocs-doctor/1.0`}})).ok}:{url:e,ok:r.ok}}catch(t){return{url:e,ok:!1,error:t.message}}},a=Array.from(n.keys()),c=[];for(let e=0;e<a.length;e+=l){let t=a.slice(e,e+l),n=await Promise.allSettled(t.map(r));c.push(...n.map(e=>e.status===`fulfilled`?e.value:{url:`unknown`,ok:!1,error:e.reason}))}for(let r of c)if(!r.ok){let a=E(e,`brokenLink`,`warning`);if(a!==`off`){let o=n.get(r.url)||[];for(let n of o)t.push({file:i(s.relative(e.docsDir,n)),level:a,message:`Broken external link: "${r.url}"`,suggestion:`Verify the URL or update it if it's permanently down. Error: ${r.error||`Status >= 400`}`})}}}return t}async function z(e){let t=[];if(!e.doctorConfig.checks.i18n.enabled||!e.config.i18n)return t;let{defaultLocale:n,locales:r}=e.config.i18n,a=Object.keys(r),o=a.filter(e=>e!==n);for(let r of e.files){let l=i(s.relative(e.docsDir,r)),u=l.split(`/`),d=u[0];if(d===n){let n=u.slice(1).join(`/`);for(let i of o){let a=s.join(e.docsDir,i,n);if(!j(a)){let o=E(e,`missingTranslation`,`warning`);o!==`off`&&t.push({file:l,level:o,message:`Missing translation for locale "${i}"`,suggestion:`Create a version at "${i}/${n}".`,fix:async()=>{let e=s.dirname(a);c.existsSync(e)||c.mkdirSync(e,{recursive:!0}),c.copyFileSync(r,a)}})}}}else if(a.includes(d)){let i=u.slice(1).join(`/`);if(!j(s.join(e.docsDir,n,i))){let a=E(e,`missingTranslation`,`low`);a!==`off`&&t.push({file:l,level:a,message:`Orphaned translation (source missing in "${n}")`,suggestion:`Remove this file or create the source at "${n}/${i}".`,fix:async()=>{c.unlinkSync(r)}})}}}return t}async function B(e){let t=[];if(!e.config.theme.sidebar)return t;let n=new Set,r=e.config.theme.sidebar;for(let[i,a]of Object.entries(r))for(let r of a){if(!r.text){let n=E(e,`invalidFrontmatter`,`warning`);n!==`off`&&t.push({file:`boltdocs.config.ts`,level:n,message:`Sidebar item in group "${i}" is missing a label.`,suggestion:`Add a "text" property to the sidebar item.`})}if(r.link&&(n.add(r.link),!e.routeIndex.has(r.link))){let{bestMatch:n,similarity:i}=I(r.link,e.linkTree.routes),a=i>.6,o=E(e,`brokenLink`,`high`);o!==`off`&&t.push({file:`boltdocs.config.ts`,level:o,message:`Broken sidebar link: "${r.link}"`,suggestion:a?`Did you mean "${n}"?`:`Ensure the route exists and is correctly formatted.`})}}for(let r of e.linkTree.routes)if(!(r===`/`||r===``)&&!n.has(r)){let n=E(e,`orphanedPage`,`low`);n!==`off`&&t.push({file:`Sidebar`,level:n,message:`Orphaned page found: "${r}" is not linked in the sidebar.`,suggestion:`Consider adding it to the sidebar for better discoverability.`})}return t}async function V(e){let t=[],n=e.doctorConfig.checks.performance;if(!n?.enabled)return t;let r=s.resolve(e.root,`.boltdocs`,`reports`,`performance.json`);if(!c.existsSync(r))return t.push({file:`(build)`,level:E(e,`budgetExceeded`,`warning`),message:"Performance metrics not found. Run `boltdocs build` first."}),t;let i;try{i=JSON.parse(c.readFileSync(r,`utf-8`))}catch{return t.push({file:`(build)`,level:E(e,`budgetExceeded`,`warning`),message:`Failed to parse performance metrics file.`}),t}let a=n.budgets,o=E(e,`budgetExceeded`,`warning`),l=T(a?.maxJSBundleSize,1/0),u=T(a?.maxCSSBundleSize,1/0),d=T(a?.maxPageHTMLSize,1/0),f=a?.maxImagesKB??1/0,p=a?.maxBuildTime??1/0,m=a?.maxFontCount??1/0;if(l!==1/0&&i.totalJSBundleSize>l){let e=(i.totalJSBundleSize/1024).toFixed(0),n=(l/1024).toFixed(0);t.push({file:`(build)`,level:o,message:`JS bundle size exceeds budget: ${e}kb > ${n}kb`,suggestion:`Code-split large dependencies or lazy-load route components.`})}if(u!==1/0&&i.totalCSSBundleSize>u){let e=(i.totalCSSBundleSize/1024).toFixed(0),n=(u/1024).toFixed(0);t.push({file:`(build)`,level:o,message:`CSS bundle size exceeds budget: ${e}kb > ${n}kb`,suggestion:`Remove unused styles or split CSS by route.`})}if(d!==1/0){for(let e of i.pages)if(e.htmlSize>d){let n=(e.htmlSize/1024).toFixed(0),r=(d/1024).toFixed(0);t.push({file:e.route,level:o,message:`Page HTML size exceeds budget: ${n}kb > ${r}kb`,suggestion:`Reduce the amount of inline content or split into sub-pages.`})}}let h=f*1024;if(f!==1/0&&i.totalImagesSize>h){let e=(i.totalImagesSize/1024).toFixed(0);t.push({file:`(build)`,level:o,message:`Image assets exceed budget: ${e}kb > ${f}kb`,suggestion:`Optimize images with lossy compression or use next-gen formats (webp/avif).`})}if(p!==1/0&&i.buildTime>p){let e=(i.buildTime/1e3).toFixed(1),n=(p/1e3).toFixed(1);t.push({file:`(build)`,level:o,message:`Build time exceeds budget: ${e}s > ${n}s`,suggestion:"Check for large unoptimized assets or increase `concurrency` in SSG options."})}return m!==1/0&&i.fontCount>m&&t.push({file:`(build)`,level:o,message:`Font files exceed budget: ${i.fontCount} > ${m}`,suggestion:`Reduce the number of font families or use variable fonts.`}),t}var H=e({DEFAULT_DOCTOR_CONFIG:()=>w,checkI18n:()=>z,checkLinks:()=>R,checkMetadata:()=>L,checkPerformance:()=>V,checkSidebar:()=>B,doctorAction:()=>W,doctorInit:()=>U,generateLinkTree:()=>N,loadDoctorConfig:()=>M});async function U(e){let t=s.resolve(e,`doctor.json`);if(c.existsSync(t)){b(`"doctor.json" already exists at ${e}.`);return}try{c.writeFileSync(t,JSON.stringify(w,null,2)),v(`Created "doctor.json" with default configuration.`)}catch(e){m(`Failed to create "doctor.json": ${e}`)}}async function W(e=process.cwd(),t={}){if(t.init){await U(e);return}o();try{let n=await M(e),{format:r}=n.reporting,i=performance.now(),o=await a(`docs`,e),b=s.resolve(e,`docs`);c.existsSync(b)||(r===`pretty`&&m(`Docs dir not found at ${b}`),process.exit(1)),r===`pretty`&&console.log(p([` ${u.dim(`Docs dir:`)} ${b}`,` ${u.dim(`Reports:`)} ${e}/.boltdocs/reports/`],{title:`✦ DOCTOR — Documentation Health Check`})),r===`pretty`&&h(u.dim(`🔍 Discovering files and routes...`));let C=S(n.exclude||[]),w=await new x().withFullPaths().filter(e=>{if(!(e.endsWith(`.md`)||e.endsWith(`.mdx`)))return!1;let t=s.relative(b,e).replace(/\\/g,`/`),n=t.split(`/`).some(e=>e.startsWith(`_`)&&e!==`_index.md`&&e!==`_index.mdx`);return!C(t)&&!n}).crawl(b).withPromise();for(let e of w)A.set(e,!0);let T=await N(b,e,o,w),E=o.base||`/`,O=E===`/`?``:E.endsWith(`/`)?E.slice(0,-1):E,k={root:e,docsDir:b,config:o,doctorConfig:n,linkTree:T,files:w,options:t,routeIndex:new Set(T.routes),routeIndexWithSlash:new Set(T.routes.map(e=>e.endsWith(`/`)?e:e+`/`)),routeIndexWithoutSlash:new Set(T.routes.map(e=>e.endsWith(`/`)?e.slice(0,-1):e)),basePrefix:O};r===`pretty`&&h(u.dim(`🧪 Running diagnostic checks in parallel...`));let j=[L(k),R(k),z(k),B(k)];t.budget&&j.push(V(k));let[P,F,I,H,...U]=await Promise.all(j),W=t.budget?U[0]:[],G=[...P,...F,...I,...H,...W];if(r===`pretty`){let e=[{label:`Metadata checks ${P.length>0?`— ${P.length} issue${P.length===1?``:`s`}`:`— OK`}`,done:P.length===0},{label:`Link checks ${F.length>0?`— ${F.length} issue${F.length===1?``:`s`}`:`— OK`}`,done:F.length===0},{label:`i18n checks ${I.length>0?`— ${I.length} issue${I.length===1?``:`s`}`:`— OK`}`,done:I.length===0},{label:`Sidebar checks ${H.length>0?`— ${H.length} issue${H.length===1?``:`s`}`:`— OK`}`,done:H.length===0}];t.budget&&e.push({label:`Performance budget ${W.length>0?`— ${W.length} issue${W.length===1?``:`s`}`:`— OK`}`,done:W.length===0}),console.log(`\n${y(e)}`)}let K=0;if(t.fix){for(let e of G)if(e.fix){if(k.doctorConfig.fix.confirmChanges&&!await d(`Fix issue in "${e.file}": ${e.message}?`))continue;if(k.doctorConfig.fix.backupFiles){let t=s.resolve(k.docsDir,e.file);c.existsSync(t)&&await D(t,s.resolve(k.root,k.doctorConfig.fix.backupPath))}await e.fix(),K++}}let q=((performance.now()-i)/1e3).toFixed(2),J=G.filter(e=>e.level===`high`).length,Y=G.filter(e=>e.level===`warning`).length,X=G.filter(e=>e.level===`low`).length,Z={summary:{total:G.length,high:J,warning:Y,low:X,fixed:K,duration:q},issues:G.map(e=>({...e,fix:void 0}))};if(n.reporting.outputFile){let t=s.resolve(e,n.reporting.outputFile);c.existsSync(s.dirname(t))||c.mkdirSync(s.dirname(t),{recursive:!0}),c.writeFileSync(t,JSON.stringify(Z,null,2))}if(r===`json`)console.log(JSON.stringify(Z,null,2));else if(r===`pretty`){let e=G.reduce((e,t)=>(e[t.file]||(e[t.file]=[]),e[t.file].push(t),e),{});if(G.length>0){f();for(let[n,r]of Object.entries(e)){let e=[];for(let n of r){let r=n.level===`high`?`❌`:n.level===`warning`?`⚠️`:`ℹ️`,i=n.level===`high`?u.red:n.level===`warning`?u.yellow:u.blue;e.push(`${r} ${i(n.level.toUpperCase())}: ${n.message}`),n.suggestion&&e.push(` ${u.dim(`💡 ${n.suggestion}`)}`),t.fix&&n.fix&&e.push(` ${u.green(`✅ Fixed automatically`)}`)}console.log(`\n${_(e,{title:`📄 ${n}`})}`)}f()}if(G.length===0)console.log(g([` Everything looks perfect!`,` Your documentation is in great shape.`,``,` ${u.dim(`Scanned ${w.length} file${w.length===1?``:`s`} in ${q}s`)}`],{title:`✨ Documentation Health Check`}));else{let e=[];J>0&&e.push(u.red(`${J} Critical Error${J===1?``:`s`}`)),Y>0&&e.push(u.yellow(`${Y} Warning${Y===1?``:`s`}`)),X>0&&e.push(u.blue(`${X} Improvement${X===1?``:`s`}`));let t=[...l(e).split(`
|
|
6
|
+
import{A as e}from"./node-JQ6C-CBi.mjs";import{a as t,d as n,g as r,u as i}from"./utils-DexBrwiP.mjs";import{n as a}from"./config-BCqkPkjC.mjs";import{t as o}from"./node/cli-entry.mjs";import s from"node:path";import c from"node:fs";import{bullet as l,colors as u,confirm as d,dividerLog as f,double as p,error as m,info as h,round as g,single as _,success as v,tasks as y,warn as b}from"@bdocs/dui";import{fdir as x}from"fdir";import S from"picomatch";import{distance as C}from"fastest-levenshtein";const w={$schema:`https://boltdocs.vercel.app/schemas/doctor-config.schema.json`,checks:{metadata:{enabled:!0,titleMin:10,titleMax:60,descriptionMin:50,required:[`title`,`description`],optional:[],validateDates:!1},links:{internal:!0,external:!1,timeout:1e4,concurrency:10,ignore:[]},i18n:{enabled:!0},performance:{enabled:!0,budgets:{maxJSBundleSize:`200kb`,maxCSSBundleSize:`30kb`,maxPageHTMLSize:`80kb`,maxImagesKB:500,maxBuildTime:3e4,maxFontCount:3}}},fix:{confirmChanges:!1,backupFiles:!1,backupPath:`.boltdocs/backups`},reporting:{format:`pretty`,outputFile:`.boltdocs/reports/doctor.json`,failOnError:!1,maxWarnings:-1},severity:{missingTranslation:`warning`,brokenLink:`high`,brokenAnchor:`warning`,largeFile:`warning`,orphanedPage:`low`,duplicateTitle:`low`,shortMetadata:`low`,missingMetadata:`warning`,malformedFrontmatter:`high`,invalidFrontmatter:`high`,budgetExceeded:`warning`},exclude:[]};function T(e,t){if(e==null)return t;if(typeof e==`number`)return e;let n=e.toLowerCase().match(/^(\d+(?:\.\d+)?)\s*(b|kb|mb|gb)?$/);if(!n)return t;let r=Number.parseFloat(n[1]),i=n[2]||`b`;return Math.round(r*({b:1,kb:1024,mb:1024*1024,gb:1024*1024*1024}[i]||1))}function E(e,t,n){return e.doctorConfig.severity[t]||n}async function D(e,t){c.existsSync(t)||c.mkdirSync(t,{recursive:!0});let n=s.basename(e),r=new Date().toISOString().replace(/[:.]/g,`-`),i=s.join(t,`${n}.${r}.bak`);c.copyFileSync(e,i)}const O=new Map;function k(e){let t=O.get(e);if(t)return t;let r=(async()=>{let t=await n(e,!1);return{raw:t.raw,data:t.data,content:t.content}})();return O.set(e,r),r}const A=new Map;function j(e){if(A.has(e))return A.get(e);let t=!1;try{t=c.existsSync(e)&&c.statSync(e).isFile()}catch{t=!1}return A.set(e,t),t}async function M(e){let t=s.resolve(e,`doctor.json`);if(c.existsSync(t))try{let e=JSON.parse(c.readFileSync(t,`utf-8`));return{...w,...e,checks:{...w.checks,...e.checks,metadata:{...w.checks.metadata,...e.checks?.metadata},links:{...w.checks.links,...e.checks?.links},i18n:{...w.checks.i18n,...e.checks?.i18n},performance:{...w.checks.performance,...e.checks?.performance,budgets:{...w.checks.performance?.budgets,...e.checks?.performance?.budgets}}},fix:{...w.fix,...e.fix},reporting:{...w.reporting,...e.reporting},severity:{...w.severity,...e.severity},exclude:[...w.exclude,...e.exclude||[]]}}catch(e){b(`Failed to parse doctor.json: ${e}`)}return w}async function N(e,n=process.cwd(),r,i){let a=s.resolve(n,`.boltdocs`,`generated`);c.existsSync(a)||c.mkdirSync(a,{recursive:!0});let o=i;o||=await new x().withFullPaths().filter(e=>e.endsWith(`.md`)||e.endsWith(`.mdx`)).crawl(e).withPromise();let l=r?.base||`/docs`,u=[];for(let n=0;n<o.length;n+=100){let r=o.slice(n,n+100),i=await Promise.all(r.map(async n=>{let r=s.isAbsolute(n)?n:s.resolve(e,n),i=s.relative(e,r),{data:a}=await k(r),o;return o=a.permalink?a.permalink.startsWith(`/`)?a.permalink:`/${a.permalink}`:t(i),l===`/`?o:(l.endsWith(`/`)?l:l+`/`)+(o.startsWith(`/`)?o.substring(1):o)}));u.push(...i)}u.includes(l)||u.push(l);let d={routes:Array.from(new Set(u)).sort(),timestamp:Date.now()};return c.writeFileSync(s.resolve(a,`link-tree.json`),JSON.stringify(d,null,2)),d}function P(e,t){if(e===t)return 1;if(!e||!t)return 0;let n=e.split(`/`).filter(Boolean),r=t.split(`/`).filter(Boolean),i=n[n.length-1]||``,a=r[r.length-1]||``;if(i===a){let e=0;for(let t of n)r.includes(t)&&e++;return e>=2?.99:.9}if(i.length>3&&a.length>3&&(a.startsWith(i)||i.startsWith(a))&&Math.min(i.length,a.length)/Math.max(i.length,a.length)>.5)return .88;let o=1-C(i,a)/Math.max(i.length,a.length);return o>.8?o*.95:1-C(e,t)/Math.max(e.length,t.length)}const F=new Map;function I(e,t){if(F.has(e))return F.get(e);let n=e.length,r=``,i=0;for(let a of t){if(a===e)continue;let t=a.length,o=Math.max(n,t);if(o===0)continue;let s=1-Math.abs(n-t)/o;if(s<=.8&&s<=i)continue;let c=P(e,a);c>i&&(i=c,r=a)}let a={bestMatch:r,similarity:i};return F.set(e,a),a}async function L(e){let t=[];if(!e.doctorConfig.checks.metadata.enabled)return t;let{titleMin:n,titleMax:a,descriptionMin:o}=e.doctorConfig.checks.metadata,c=new Map;for(let l of e.files){let u=i(s.relative(e.docsDir,l));try{let{raw:i,data:s}=await k(l);if(i.trim().startsWith(`---`)&&i.split(`---`).length>=3&&Object.keys(s).length===0){let n=E(e,`malformedFrontmatter`,`high`);n!==`off`&&t.push({file:u,level:n,message:`Malformed frontmatter (YAML parsing failed).`,suggestion:`Check your YAML syntax for indentation or unquoted special characters.`})}let d=r.safeParse(s);if(!d.success){let n=E(e,`invalidFrontmatter`,`high`);if(n!==`off`)for(let e of d.error.issues)t.push({file:u,level:n,message:`Invalid frontmatter field "${e.path.join(`.`)}": ${e.message}`,suggestion:`Ensure the field follows the correct type.`})}let f=Array.from(new Set([`title`,...e.doctorConfig.checks.metadata.required]));for(let n of f)if(s[n]===void 0){let r=E(e,`missingMetadata`,`warning`);r!==`off`&&t.push({file:u,level:r,message:`Missing required frontmatter field: "${n}".`,suggestion:`Add the "${n}" field to your frontmatter.`})}if(e.doctorConfig.checks.metadata.validateDates){let n=[`date`,`lastUpdated`,...e.doctorConfig.checks.metadata.optional.filter(e=>e.toLowerCase().includes(`date`))];for(let r of n)if(s[r]&&Number.isNaN(Date.parse(String(s[r])))){let n=E(e,`invalidFrontmatter`,`high`);n!==`off`&&t.push({file:u,level:n,message:`Invalid date format in field "${r}": "${s[r]}".`,suggestion:`Use a valid ISO date format (e.g., YYYY-MM-DD).`})}}if(s.title){let r=String(s.title);if(r.length<n){let i=E(e,`shortMetadata`,`low`);i!==`off`&&t.push({file:u,level:i,message:`Title is too short (${r.length} chars).`,suggestion:`Titles should be at least ${n} characters for better SEO.`})}else if(r.length>a){let n=E(e,`shortMetadata`,`low`);n!==`off`&&t.push({file:u,level:n,message:`Title is too long (${r.length} chars).`,suggestion:`Titles should be under ${a} characters.`})}let i=c.get(r)||[];i.push(u),c.set(r,i)}if(s.description&&String(s.description).length<o){let n=E(e,`shortMetadata`,`low`);n!==`off`&&t.push({file:u,level:n,message:`Description is very short.`,suggestion:`Descriptions should ideally be at least ${o} characters.`})}}catch(n){if(n instanceof Error){let r=E(e,`malformedFrontmatter`,`high`);r!==`off`&&t.push({file:u,level:r,message:`Malformed frontmatter (YAML error): ${n.message}`,suggestion:`Check your YAML syntax for indentation or unquoted special characters.`})}}}for(let[n,r]of c.entries())if(r.length>1){let i=E(e,`duplicateTitle`,`low`);if(i!==`off`)for(let e of r)t.push({file:e,level:i,message:`Duplicate title found: "${n}"`,suggestion:`Ensure each page has a unique title. Also used in: ${r.filter(t=>t!==e).join(`, `)}`})}return t}async function R(e){let t=[],{internal:n,external:r,ignore:a,timeout:o,concurrency:l}=e.doctorConfig.checks.links;if(!n&&!r&&!e.options.checkExternal)return t;let d=/(?:\[.*?\]\((.*?)\))|(?:href=["']([^"']+)["'])/g,f=new Set,p=5e5;for(let o of e.files){let l=i(s.relative(e.docsDir,o)),{content:u}=await k(o),m=[...(u.length>p?u.slice(0,p):u).replace(/```[\s\S]*?```/g,``).replace(/`[^`\n]*`/g,``).matchAll(d)];for(let i of m){let u=i[1]||i[2],d=!!i[1];if(!u||a.some(e=>u.includes(e)))continue;if(/^https?:\/\//i.test(u)){(r||e.options.checkExternal)&&f.add({url:u,file:o});continue}if(!n||/^(mailto|tel|#)/i.test(u))continue;let p;try{p=decodeURIComponent(u.split(`#`)[0].split(`?`)[0])}catch{p=u.split(`#`)[0].split(`?`)[0]}if(!p)continue;let m=!1,h=``;if(p.startsWith(`/`))if(e.routeIndex.has(p)||e.routeIndexWithSlash.has(p)||e.routeIndexWithoutSlash.has(p))m=!0;else{let t=e.basePrefix+(p.startsWith(`/`)?p:`/`+p);if(e.routeIndex.has(t)||e.routeIndexWithSlash.has(t))m=!1,h=t;else{let t=e.config.base!==`/`&&p.startsWith(e.config.base||`/`)?p.substring((e.config.base||`/`).length):p,n=t.startsWith(`/`)?t.substring(1):t;h=s.join(e.docsDir,n),m=[``,`.md`,`.mdx`,`/index.md`,`/index.mdx`].some(e=>j(h+e))}}else h=s.resolve(s.dirname(o),p),m=[``,`.md`,`.mdx`,`/index.md`,`/index.mdx`].some(e=>j(h+e));if(!m){let{bestMatch:n,similarity:r}=I(p,e.linkTree.routes),i=!1,a=e.basePrefix+(p.startsWith(`/`)?p:`/`+p);(e.routeIndex.has(a)||e.routeIndexWithSlash.has(a))&&(n=a,r=1,i=!0);let s=r>.6||i,f=r>.75&&n!==p||i,m=E(e,`brokenLink`,`high`);m!==`off`&&t.push({file:l,level:m,message:`Broken internal link: "${u}"`,suggestion:s?`Did you mean "${n}"?`:`Ensure the target exists or check for typos.`,fix:f?async()=>{let e=u.includes(`#`)?`#`+u.split(`#`)[1]:``,t=d?`(${u})`:`href="${u}"`,r=d?`(${n}${e})`:`href="${n}${e}"`,i=c.readFileSync(o,`utf-8`).replace(t,r);c.writeFileSync(o,i),O.delete(o)}:void 0})}}}if(f.size>0){h(u.gray(`Verifying ${f.size} external links...`));let n=new Map;for(let e of f)n.has(e.url)||n.set(e.url,[]),n.get(e.url).push(e.file);let r=async e=>{try{let t=new AbortController,n=setTimeout(()=>t.abort(),o),r=await fetch(e,{method:`HEAD`,signal:t.signal,headers:{"User-Agent":`boltdocs-doctor/1.0`}});return clearTimeout(n),!r.ok&&r.status!==404?{url:e,ok:(await fetch(e,{method:`GET`,signal:t.signal,headers:{"User-Agent":`boltdocs-doctor/1.0`}})).ok}:{url:e,ok:r.ok}}catch(t){return{url:e,ok:!1,error:t.message}}},a=Array.from(n.keys()),c=[];for(let e=0;e<a.length;e+=l){let t=a.slice(e,e+l),n=await Promise.allSettled(t.map(r));c.push(...n.map(e=>e.status===`fulfilled`?e.value:{url:`unknown`,ok:!1,error:e.reason}))}for(let r of c)if(!r.ok){let a=E(e,`brokenLink`,`warning`);if(a!==`off`){let o=n.get(r.url)||[];for(let n of o)t.push({file:i(s.relative(e.docsDir,n)),level:a,message:`Broken external link: "${r.url}"`,suggestion:`Verify the URL or update it if it's permanently down. Error: ${r.error||`Status >= 400`}`})}}}return t}async function z(e){let t=[];if(!e.doctorConfig.checks.i18n.enabled||!e.config.i18n)return t;let{defaultLocale:n,locales:r}=e.config.i18n,a=Object.keys(r),o=a.filter(e=>e!==n);for(let r of e.files){let l=i(s.relative(e.docsDir,r)),u=l.split(`/`),d=u[0];if(d===n){let n=u.slice(1).join(`/`);for(let i of o){let a=s.join(e.docsDir,i,n);if(!j(a)){let o=E(e,`missingTranslation`,`warning`);o!==`off`&&t.push({file:l,level:o,message:`Missing translation for locale "${i}"`,suggestion:`Create a version at "${i}/${n}".`,fix:async()=>{let e=s.dirname(a);c.existsSync(e)||c.mkdirSync(e,{recursive:!0}),c.copyFileSync(r,a)}})}}}else if(a.includes(d)){let i=u.slice(1).join(`/`);if(!j(s.join(e.docsDir,n,i))){let a=E(e,`missingTranslation`,`low`);a!==`off`&&t.push({file:l,level:a,message:`Orphaned translation (source missing in "${n}")`,suggestion:`Remove this file or create the source at "${n}/${i}".`,fix:async()=>{c.unlinkSync(r)}})}}}return t}async function B(e){let t=[];if(!e.config.theme.sidebar)return t;let n=new Set,r=e.config.theme.sidebar;for(let[i,a]of Object.entries(r))for(let r of a){if(!r.text){let n=E(e,`invalidFrontmatter`,`warning`);n!==`off`&&t.push({file:`boltdocs.config.ts`,level:n,message:`Sidebar item in group "${i}" is missing a label.`,suggestion:`Add a "text" property to the sidebar item.`})}if(r.link&&(n.add(r.link),!e.routeIndex.has(r.link))){let{bestMatch:n,similarity:i}=I(r.link,e.linkTree.routes),a=i>.6,o=E(e,`brokenLink`,`high`);o!==`off`&&t.push({file:`boltdocs.config.ts`,level:o,message:`Broken sidebar link: "${r.link}"`,suggestion:a?`Did you mean "${n}"?`:`Ensure the route exists and is correctly formatted.`})}}for(let r of e.linkTree.routes)if(!(r===`/`||r===``)&&!n.has(r)){let n=E(e,`orphanedPage`,`low`);n!==`off`&&t.push({file:`Sidebar`,level:n,message:`Orphaned page found: "${r}" is not linked in the sidebar.`,suggestion:`Consider adding it to the sidebar for better discoverability.`})}return t}async function V(e){let t=[],n=e.doctorConfig.checks.performance;if(!n?.enabled)return t;let r=s.resolve(e.root,`.boltdocs`,`reports`,`performance.json`);if(!c.existsSync(r))return t.push({file:`(build)`,level:E(e,`budgetExceeded`,`warning`),message:"Performance metrics not found. Run `boltdocs build` first."}),t;let i;try{i=JSON.parse(c.readFileSync(r,`utf-8`))}catch{return t.push({file:`(build)`,level:E(e,`budgetExceeded`,`warning`),message:`Failed to parse performance metrics file.`}),t}let a=n.budgets,o=E(e,`budgetExceeded`,`warning`),l=T(a?.maxJSBundleSize,1/0),u=T(a?.maxCSSBundleSize,1/0),d=T(a?.maxPageHTMLSize,1/0),f=a?.maxImagesKB??1/0,p=a?.maxBuildTime??1/0,m=a?.maxFontCount??1/0;if(l!==1/0&&i.totalJSBundleSize>l){let e=(i.totalJSBundleSize/1024).toFixed(0),n=(l/1024).toFixed(0);t.push({file:`(build)`,level:o,message:`JS bundle size exceeds budget: ${e}kb > ${n}kb`,suggestion:`Code-split large dependencies or lazy-load route components.`})}if(u!==1/0&&i.totalCSSBundleSize>u){let e=(i.totalCSSBundleSize/1024).toFixed(0),n=(u/1024).toFixed(0);t.push({file:`(build)`,level:o,message:`CSS bundle size exceeds budget: ${e}kb > ${n}kb`,suggestion:`Remove unused styles or split CSS by route.`})}if(d!==1/0){for(let e of i.pages)if(e.htmlSize>d){let n=(e.htmlSize/1024).toFixed(0),r=(d/1024).toFixed(0);t.push({file:e.route,level:o,message:`Page HTML size exceeds budget: ${n}kb > ${r}kb`,suggestion:`Reduce the amount of inline content or split into sub-pages.`})}}let h=f*1024;if(f!==1/0&&i.totalImagesSize>h){let e=(i.totalImagesSize/1024).toFixed(0);t.push({file:`(build)`,level:o,message:`Image assets exceed budget: ${e}kb > ${f}kb`,suggestion:`Optimize images with lossy compression or use next-gen formats (webp/avif).`})}if(p!==1/0&&i.buildTime>p){let e=(i.buildTime/1e3).toFixed(1),n=(p/1e3).toFixed(1);t.push({file:`(build)`,level:o,message:`Build time exceeds budget: ${e}s > ${n}s`,suggestion:"Check for large unoptimized assets or increase `concurrency` in SSG options."})}return m!==1/0&&i.fontCount>m&&t.push({file:`(build)`,level:o,message:`Font files exceed budget: ${i.fontCount} > ${m}`,suggestion:`Reduce the number of font families or use variable fonts.`}),t}var H=e({DEFAULT_DOCTOR_CONFIG:()=>w,checkI18n:()=>z,checkLinks:()=>R,checkMetadata:()=>L,checkPerformance:()=>V,checkSidebar:()=>B,doctorAction:()=>W,doctorInit:()=>U,generateLinkTree:()=>N,loadDoctorConfig:()=>M});async function U(e){let t=s.resolve(e,`doctor.json`);if(c.existsSync(t)){b(`"doctor.json" already exists at ${e}.`);return}try{c.writeFileSync(t,JSON.stringify(w,null,2)),v(`Created "doctor.json" with default configuration.`)}catch(e){m(`Failed to create "doctor.json": ${e}`)}}async function W(e=process.cwd(),t={}){if(t.init){await U(e);return}o();try{let n=await M(e),{format:r}=n.reporting,i=performance.now(),o=await a(`docs`,e),b=s.resolve(e,`docs`);c.existsSync(b)||(r===`pretty`&&m(`Docs dir not found at ${b}`),process.exit(1)),r===`pretty`&&console.log(p([` ${u.dim(`Docs dir:`)} ${b}`,` ${u.dim(`Reports:`)} ${e}/.boltdocs/reports/`],{title:`✦ DOCTOR — Documentation Health Check`})),r===`pretty`&&h(u.dim(`🔍 Discovering files and routes...`));let C=S(n.exclude||[]),w=await new x().withFullPaths().filter(e=>{if(!(e.endsWith(`.md`)||e.endsWith(`.mdx`)))return!1;let t=s.relative(b,e).replace(/\\/g,`/`),n=t.split(`/`).some(e=>e.startsWith(`_`)&&e!==`_index.md`&&e!==`_index.mdx`);return!C(t)&&!n}).crawl(b).withPromise();for(let e of w)A.set(e,!0);let T=await N(b,e,o,w),E=o.base||`/`,O=E===`/`?``:E.endsWith(`/`)?E.slice(0,-1):E,k={root:e,docsDir:b,config:o,doctorConfig:n,linkTree:T,files:w,options:t,routeIndex:new Set(T.routes),routeIndexWithSlash:new Set(T.routes.map(e=>e.endsWith(`/`)?e:e+`/`)),routeIndexWithoutSlash:new Set(T.routes.map(e=>e.endsWith(`/`)?e.slice(0,-1):e)),basePrefix:O};r===`pretty`&&h(u.dim(`🧪 Running diagnostic checks in parallel...`));let j=[L(k),R(k),z(k),B(k)];t.budget&&j.push(V(k));let[P,F,I,H,...U]=await Promise.all(j),W=t.budget?U[0]:[],G=[...P,...F,...I,...H,...W];if(r===`pretty`){let e=[{label:`Metadata checks ${P.length>0?`— ${P.length} issue${P.length===1?``:`s`}`:`— OK`}`,done:P.length===0},{label:`Link checks ${F.length>0?`— ${F.length} issue${F.length===1?``:`s`}`:`— OK`}`,done:F.length===0},{label:`i18n checks ${I.length>0?`— ${I.length} issue${I.length===1?``:`s`}`:`— OK`}`,done:I.length===0},{label:`Sidebar checks ${H.length>0?`— ${H.length} issue${H.length===1?``:`s`}`:`— OK`}`,done:H.length===0}];t.budget&&e.push({label:`Performance budget ${W.length>0?`— ${W.length} issue${W.length===1?``:`s`}`:`— OK`}`,done:W.length===0}),console.log(`\n${y(e)}`)}let K=0;if(t.fix){for(let e of G)if(e.fix){if(k.doctorConfig.fix.confirmChanges&&!await d(`Fix issue in "${e.file}": ${e.message}?`))continue;if(k.doctorConfig.fix.backupFiles){let t=s.resolve(k.docsDir,e.file);c.existsSync(t)&&await D(t,s.resolve(k.root,k.doctorConfig.fix.backupPath))}await e.fix(),K++}}let q=((performance.now()-i)/1e3).toFixed(2),J=G.filter(e=>e.level===`high`).length,Y=G.filter(e=>e.level===`warning`).length,X=G.filter(e=>e.level===`low`).length,Z={summary:{total:G.length,high:J,warning:Y,low:X,fixed:K,duration:q},issues:G.map(e=>({...e,fix:void 0}))};if(n.reporting.outputFile){let t=s.resolve(e,n.reporting.outputFile);c.existsSync(s.dirname(t))||c.mkdirSync(s.dirname(t),{recursive:!0}),c.writeFileSync(t,JSON.stringify(Z,null,2))}if(r===`json`)console.log(JSON.stringify(Z,null,2));else if(r===`pretty`){let e=G.reduce((e,t)=>(e[t.file]||(e[t.file]=[]),e[t.file].push(t),e),{});if(G.length>0){f();for(let[n,r]of Object.entries(e)){let e=[];for(let n of r){let r=n.level===`high`?`❌`:n.level===`warning`?`⚠️`:`ℹ️`,i=n.level===`high`?u.red:n.level===`warning`?u.yellow:u.blue;e.push(`${r} ${i(n.level.toUpperCase())}: ${n.message}`),n.suggestion&&e.push(` ${u.dim(`💡 ${n.suggestion}`)}`),t.fix&&n.fix&&e.push(` ${u.green(`✅ Fixed automatically`)}`)}console.log(`\n${_(e,{title:`📄 ${n}`})}`)}f()}if(G.length===0)console.log(g([` Everything looks perfect!`,` Your documentation is in great shape.`,``,` ${u.dim(`Scanned ${w.length} file${w.length===1?``:`s`} in ${q}s`)}`],{title:`✨ Documentation Health Check`}));else{let e=[];J>0&&e.push(u.red(`${J} Critical Error${J===1?``:`s`}`)),Y>0&&e.push(u.yellow(`${Y} Warning${Y===1?``:`s`}`)),X>0&&e.push(u.blue(`${X} Improvement${X===1?``:`s`}`));let t=[...l(e).split(`
|
|
7
7
|
`).map(e=>e.trimStart()),``,u.dim(`Scanned ${w.length} file${w.length===1?``:`s`} in ${q}s`)];console.log(`\n${p(t,{title:`Diagnosis Results`})}\n`),K>0&&v(`Successfully fixed ${K} issues automatically!`),J>0?m(`Please fix the critical errors before building for production.`):v(`No critical issues found. You are ready to go!`)}}n.reporting.failOnError&&J>0&&process.exit(1),n.reporting.maxWarnings!==-1&&Y>n.reporting.maxWarnings&&(r===`pretty`&&m(`Failed: Too many warnings (${Y} > ${n.reporting.maxWarnings})`),process.exit(1))}catch(e){m(`Doctor failed: ${e}`),process.exit(1)}}export{N as n,H as t};
|
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
* Copyright (c) 2026 Jesus Alcala
|
|
4
4
|
* Licensed under the MIT License.
|
|
5
5
|
*/
|
|
6
|
-
import{
|
|
6
|
+
import{A as e}from"./node-JQ6C-CBi.mjs";var t=e({SECURITY_HEADERS:()=>n});const n={"X-Content-Type-Options":`nosniff`,"X-Frame-Options":`DENY`,"X-XSS-Protection":`1; mode=block`,"Referrer-Policy":`strict-origin-when-cross-origin`,"Permissions-Policy":`camera=(), microphone=(), geolocation=()`,"Strict-Transport-Security":`max-age=31536000; includeSubDomains`};export{t as n,n as t};
|
|
@@ -21,10 +21,7 @@ function ThemeProvider({ children }) {
|
|
|
21
21
|
} catch {}
|
|
22
22
|
return "system";
|
|
23
23
|
});
|
|
24
|
-
const [resolvedTheme, setResolvedTheme] = useState(
|
|
25
|
-
if (typeof window !== "undefined" && typeof document !== "undefined" && document.documentElement) return document.documentElement.classList.contains("dark") ? "dark" : "light";
|
|
26
|
-
return "light";
|
|
27
|
-
});
|
|
24
|
+
const [resolvedTheme, setResolvedTheme] = useState("light");
|
|
28
25
|
const applyTheme = (targetTheme) => {
|
|
29
26
|
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
30
27
|
const isDark = targetTheme === "dark" || targetTheme === "system" && mediaQuery.matches;
|
|
@@ -22,10 +22,7 @@ function ThemeProvider({ children }) {
|
|
|
22
22
|
} catch {}
|
|
23
23
|
return "system";
|
|
24
24
|
});
|
|
25
|
-
const [resolvedTheme, setResolvedTheme] = (0, react.useState)(
|
|
26
|
-
if (typeof window !== "undefined" && typeof document !== "undefined" && document.documentElement) return document.documentElement.classList.contains("dark") ? "dark" : "light";
|
|
27
|
-
return "light";
|
|
28
|
-
});
|
|
25
|
+
const [resolvedTheme, setResolvedTheme] = (0, react.useState)("light");
|
|
29
26
|
const applyTheme = (targetTheme) => {
|
|
30
27
|
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
31
28
|
const isDark = targetTheme === "dark" || targetTheme === "system" && mediaQuery.matches;
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
|
|
2
|
+
import { VercelRequest, VercelResponse } from "@vercel/node";
|
|
3
|
+
import { HandlerEvent, HandlerResponse } from "@netlify/functions";
|
|
4
|
+
import { Writable } from "node:stream";
|
|
5
|
+
|
|
6
|
+
//#region src/node/feedback/adapters/vercel.d.ts
|
|
7
|
+
declare function handleVercelFeedback(req: VercelRequest, res: VercelResponse): Promise<VercelResponse | undefined>;
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region src/node/feedback/adapters/netlify.d.ts
|
|
10
|
+
declare function handleNetlifyFeedback(event: HandlerEvent, env?: Record<string, string | undefined>): Promise<HandlerResponse>;
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/node/feedback/adapters/web.d.ts
|
|
13
|
+
declare function handleWebFeedback(request: Request, env?: Record<string, string | undefined>): Promise<Response>;
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region ../../node_modules/.pnpm/@types+aws-lambda@8.10.161/node_modules/@types/aws-lambda/common/api-gateway.d.ts
|
|
16
|
+
// Default authorizer type, prefer using a specific type with the "...WithAuthorizer..." variant types.
|
|
17
|
+
// Note that this doesn't have to be a context from a custom lambda outhorizer, AWS also has a cognito
|
|
18
|
+
// authorizer type and could add more, so the property won't always be a string.
|
|
19
|
+
type APIGatewayEventDefaultAuthorizerContext = undefined | null | {
|
|
20
|
+
[name: string]: any;
|
|
21
|
+
};
|
|
22
|
+
// The requestContext property of both request authorizer and proxy integration events.
|
|
23
|
+
interface APIGatewayEventRequestContextWithAuthorizer<TAuthorizerContext> {
|
|
24
|
+
accountId: string;
|
|
25
|
+
apiId: string; // This one is a bit confusing: it is not actually present in authorizer calls
|
|
26
|
+
// and proxy calls without an authorizer. We model this by allowing undefined in the type,
|
|
27
|
+
// since it ends up the same and avoids breaking users that are testing the property.
|
|
28
|
+
// This lets us allow parameterizing the authorizer for proxy events that know what authorizer
|
|
29
|
+
// context values they have.
|
|
30
|
+
authorizer: TAuthorizerContext;
|
|
31
|
+
connectedAt?: number | undefined;
|
|
32
|
+
connectionId?: string | undefined;
|
|
33
|
+
domainName?: string | undefined;
|
|
34
|
+
domainPrefix?: string | undefined;
|
|
35
|
+
eventType?: string | undefined;
|
|
36
|
+
extendedRequestId?: string | undefined;
|
|
37
|
+
protocol: string;
|
|
38
|
+
httpMethod: string;
|
|
39
|
+
identity: APIGatewayEventIdentity;
|
|
40
|
+
messageDirection?: string | undefined;
|
|
41
|
+
messageId?: string | null | undefined;
|
|
42
|
+
path: string;
|
|
43
|
+
stage: string;
|
|
44
|
+
requestId: string;
|
|
45
|
+
requestTime?: string | undefined;
|
|
46
|
+
requestTimeEpoch: number;
|
|
47
|
+
resourceId: string;
|
|
48
|
+
resourcePath: string;
|
|
49
|
+
routeKey?: string | undefined;
|
|
50
|
+
}
|
|
51
|
+
interface APIGatewayEventClientCertificate {
|
|
52
|
+
clientCertPem: string;
|
|
53
|
+
serialNumber: string;
|
|
54
|
+
subjectDN: string;
|
|
55
|
+
issuerDN: string;
|
|
56
|
+
validity: {
|
|
57
|
+
notAfter: string;
|
|
58
|
+
notBefore: string;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
interface APIGatewayEventIdentity {
|
|
62
|
+
accessKey: string | null;
|
|
63
|
+
accountId: string | null;
|
|
64
|
+
apiKey: string | null;
|
|
65
|
+
apiKeyId: string | null;
|
|
66
|
+
caller: string | null;
|
|
67
|
+
clientCert: APIGatewayEventClientCertificate | null;
|
|
68
|
+
cognitoAuthenticationProvider: string | null;
|
|
69
|
+
cognitoAuthenticationType: string | null;
|
|
70
|
+
cognitoIdentityId: string | null;
|
|
71
|
+
cognitoIdentityPoolId: string | null;
|
|
72
|
+
principalOrgId: string | null;
|
|
73
|
+
sourceIp: string;
|
|
74
|
+
user: string | null;
|
|
75
|
+
userAgent: string | null;
|
|
76
|
+
userArn: string | null;
|
|
77
|
+
vpcId?: string | undefined;
|
|
78
|
+
vpceId?: string | undefined;
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region ../../node_modules/.pnpm/@types+aws-lambda@8.10.161/node_modules/@types/aws-lambda/handler.d.ts
|
|
82
|
+
/**
|
|
83
|
+
* {@link Handler} context parameter.
|
|
84
|
+
* See {@link https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html AWS documentation}.
|
|
85
|
+
*/
|
|
86
|
+
interface Context {
|
|
87
|
+
callbackWaitsForEmptyEventLoop: boolean;
|
|
88
|
+
functionName: string;
|
|
89
|
+
functionVersion: string;
|
|
90
|
+
invokedFunctionArn: string;
|
|
91
|
+
memoryLimitInMB: string;
|
|
92
|
+
awsRequestId: string;
|
|
93
|
+
logGroupName: string;
|
|
94
|
+
logStreamName: string;
|
|
95
|
+
identity?: CognitoIdentity | undefined;
|
|
96
|
+
clientContext?: ClientContext | undefined;
|
|
97
|
+
tenantId?: string | undefined;
|
|
98
|
+
getRemainingTimeInMillis(): number; // Functions for compatibility with earlier Node.js Runtime v0.10.42
|
|
99
|
+
// No longer documented, so they are deprecated, but they still work
|
|
100
|
+
// as of the 12.x runtime, so they are not removed from the types.
|
|
101
|
+
/** @deprecated Use handler callback or promise result */
|
|
102
|
+
done(error?: Error, result?: any): void;
|
|
103
|
+
/** @deprecated Use handler callback with first argument or reject a promise result */
|
|
104
|
+
fail(error: Error | string): void;
|
|
105
|
+
/** @deprecated Use handler callback with second argument or resolve a promise result */
|
|
106
|
+
succeed(messageOrObject: any): void; // Unclear what behavior this is supposed to have, I couldn't find any still extant reference,
|
|
107
|
+
// and it behaves like the above, ignoring the object parameter.
|
|
108
|
+
/** @deprecated Use handler callback or promise result */
|
|
109
|
+
succeed(message: string, object: any): void;
|
|
110
|
+
}
|
|
111
|
+
interface CognitoIdentity {
|
|
112
|
+
cognitoIdentityId: string;
|
|
113
|
+
cognitoIdentityPoolId: string;
|
|
114
|
+
}
|
|
115
|
+
interface ClientContext {
|
|
116
|
+
client: ClientContextClient;
|
|
117
|
+
custom?: any;
|
|
118
|
+
env: ClientContextEnv;
|
|
119
|
+
}
|
|
120
|
+
interface ClientContextClient {
|
|
121
|
+
installationId: string;
|
|
122
|
+
appTitle: string;
|
|
123
|
+
appVersionName: string;
|
|
124
|
+
appVersionCode: string;
|
|
125
|
+
appPackageName: string;
|
|
126
|
+
}
|
|
127
|
+
interface ClientContextEnv {
|
|
128
|
+
platformVersion: string;
|
|
129
|
+
platform: string;
|
|
130
|
+
make: string;
|
|
131
|
+
model: string;
|
|
132
|
+
locale: string;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Interface for using response streaming from AWS Lambda.
|
|
136
|
+
* To indicate to the runtime that Lambda should stream your function’s responses, you must wrap your function handler with the `awslambda.streamifyResponse()` decorator.
|
|
137
|
+
*
|
|
138
|
+
* The `streamifyResponse` decorator accepts the following additional parameter, `responseStream`, besides the default node handler parameters, `event`, and `context`.
|
|
139
|
+
* The new `responseStream` object provides a stream object that your function can write data to. Data written to this stream is sent immediately to the client. You can optionally set the Content-Type header of the response to pass additional metadata to your client about the contents of the stream.
|
|
140
|
+
*
|
|
141
|
+
* {@link https://aws.amazon.com/blogs/compute/introducing-aws-lambda-response-streaming/ AWS blog post}
|
|
142
|
+
* {@link https://docs.aws.amazon.com/lambda/latest/dg/config-rs-write-functions.html AWS documentation}
|
|
143
|
+
*
|
|
144
|
+
* @example <caption>Writing to the response stream</caption>
|
|
145
|
+
* import 'aws-lambda';
|
|
146
|
+
*
|
|
147
|
+
* export const handler = awslambda.streamifyResponse(
|
|
148
|
+
* async (event, responseStream, context) => {
|
|
149
|
+
* responseStream.setContentType("text/plain");
|
|
150
|
+
* responseStream.write("Hello, world!");
|
|
151
|
+
* responseStream.end();
|
|
152
|
+
* }
|
|
153
|
+
* );
|
|
154
|
+
*
|
|
155
|
+
* @example <caption>Using pipeline</caption>
|
|
156
|
+
* import 'aws-lambda';
|
|
157
|
+
* import { Readable } from 'stream';
|
|
158
|
+
* import { pipeline } from 'stream/promises';
|
|
159
|
+
* import zlib from 'zlib';
|
|
160
|
+
*
|
|
161
|
+
* export const handler = awslambda.streamifyResponse(
|
|
162
|
+
* async (event, responseStream, context) => {
|
|
163
|
+
* // As an example, convert event to a readable stream.
|
|
164
|
+
* const requestStream = Readable.from(Buffer.from(JSON.stringify(event)));
|
|
165
|
+
*
|
|
166
|
+
* await pipeline(requestStream, zlib.createGzip(), responseStream);
|
|
167
|
+
* }
|
|
168
|
+
* );
|
|
169
|
+
*/
|
|
170
|
+
type StreamifyHandler<TEvent = any, TResult = any> = (event: TEvent, responseStream: awslambda.HttpResponseStream, context: Context) => TResult | Promise<TResult>;
|
|
171
|
+
declare global {
|
|
172
|
+
namespace awslambda {
|
|
173
|
+
class HttpResponseStream extends Writable {
|
|
174
|
+
static from(writable: Writable, metadata: Record<string, unknown>): HttpResponseStream;
|
|
175
|
+
setContentType: (contentType: string) => void;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Decorator for using response streaming from AWS Lambda.
|
|
179
|
+
* To indicate to the runtime that Lambda should stream your function’s responses, you must wrap your function handler with the `awslambda.streamifyResponse()` decorator.
|
|
180
|
+
*
|
|
181
|
+
* The `streamifyResponse` decorator accepts the following additional parameter, `responseStream`, besides the default node handler parameters, `event`, and `context`.
|
|
182
|
+
* The new `responseStream` object provides a stream object that your function can write data to. Data written to this stream is sent immediately to the client. You can optionally set the Content-Type header of the response to pass additional metadata to your client about the contents of the stream.
|
|
183
|
+
*
|
|
184
|
+
* {@link https://aws.amazon.com/blogs/compute/introducing-aws-lambda-response-streaming/ AWS blog post}
|
|
185
|
+
* {@link https://docs.aws.amazon.com/lambda/latest/dg/config-rs-write-functions.html AWS documentation}
|
|
186
|
+
*
|
|
187
|
+
* @example <caption>Writing to the response stream</caption>
|
|
188
|
+
* import 'aws-lambda';
|
|
189
|
+
*
|
|
190
|
+
* export const handler = awslambda.streamifyResponse(
|
|
191
|
+
* async (event, responseStream, context) => {
|
|
192
|
+
* responseStream.setContentType("text/plain");
|
|
193
|
+
* responseStream.write("Hello, world!");
|
|
194
|
+
* responseStream.end();
|
|
195
|
+
* }
|
|
196
|
+
* );
|
|
197
|
+
*
|
|
198
|
+
* @example <caption>Using pipeline</caption>
|
|
199
|
+
* import 'aws-lambda';
|
|
200
|
+
* import { Readable } from 'stream';
|
|
201
|
+
* import { pipeline } from 'stream/promises';
|
|
202
|
+
* import zlib from 'zlib';
|
|
203
|
+
*
|
|
204
|
+
* export const handler = awslambda.streamifyResponse(
|
|
205
|
+
* async (event, responseStream, context) => {
|
|
206
|
+
* // As an example, convert event to a readable stream.
|
|
207
|
+
* const requestStream = Readable.from(Buffer.from(JSON.stringify(event)));
|
|
208
|
+
*
|
|
209
|
+
* await pipeline(requestStream, zlib.createGzip(), responseStream);
|
|
210
|
+
* }
|
|
211
|
+
* );
|
|
212
|
+
*/
|
|
213
|
+
function streamifyResponse<TEvent = any, TResult = void>(handler: StreamifyHandler<TEvent, TResult>): StreamifyHandler<TEvent, TResult>;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
//#endregion
|
|
217
|
+
//#region ../../node_modules/.pnpm/@types+aws-lambda@8.10.161/node_modules/@types/aws-lambda/trigger/api-gateway-proxy.d.ts
|
|
218
|
+
/**
|
|
219
|
+
* Works with Lambda Proxy Integration for Rest API or HTTP API integration Payload Format version 1.0
|
|
220
|
+
* @see - https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html
|
|
221
|
+
*/
|
|
222
|
+
type APIGatewayProxyEvent = APIGatewayProxyEventBase<APIGatewayEventDefaultAuthorizerContext>;
|
|
223
|
+
interface APIGatewayProxyEventHeaders {
|
|
224
|
+
[name: string]: string | undefined;
|
|
225
|
+
}
|
|
226
|
+
interface APIGatewayProxyEventMultiValueHeaders {
|
|
227
|
+
[name: string]: string[] | undefined;
|
|
228
|
+
}
|
|
229
|
+
interface APIGatewayProxyEventPathParameters {
|
|
230
|
+
[name: string]: string | undefined;
|
|
231
|
+
}
|
|
232
|
+
interface APIGatewayProxyEventQueryStringParameters {
|
|
233
|
+
[name: string]: string | undefined;
|
|
234
|
+
}
|
|
235
|
+
interface APIGatewayProxyEventMultiValueQueryStringParameters {
|
|
236
|
+
[name: string]: string[] | undefined;
|
|
237
|
+
}
|
|
238
|
+
interface APIGatewayProxyEventStageVariables {
|
|
239
|
+
[name: string]: string | undefined;
|
|
240
|
+
}
|
|
241
|
+
interface APIGatewayProxyEventBase<TAuthorizerContext> {
|
|
242
|
+
body: string | null;
|
|
243
|
+
headers: APIGatewayProxyEventHeaders;
|
|
244
|
+
multiValueHeaders: APIGatewayProxyEventMultiValueHeaders;
|
|
245
|
+
httpMethod: string;
|
|
246
|
+
isBase64Encoded: boolean;
|
|
247
|
+
path: string;
|
|
248
|
+
pathParameters: APIGatewayProxyEventPathParameters | null;
|
|
249
|
+
queryStringParameters: APIGatewayProxyEventQueryStringParameters | null;
|
|
250
|
+
multiValueQueryStringParameters: APIGatewayProxyEventMultiValueQueryStringParameters | null;
|
|
251
|
+
stageVariables: APIGatewayProxyEventStageVariables | null;
|
|
252
|
+
requestContext: APIGatewayEventRequestContextWithAuthorizer<TAuthorizerContext>;
|
|
253
|
+
resource: string;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Works with Lambda Proxy Integration for Rest API or HTTP API integration Payload Format version 1.0
|
|
257
|
+
* @see - https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html
|
|
258
|
+
*/
|
|
259
|
+
interface APIGatewayProxyResult {
|
|
260
|
+
statusCode: number;
|
|
261
|
+
headers?: {
|
|
262
|
+
[header: string]: boolean | number | string;
|
|
263
|
+
} | undefined;
|
|
264
|
+
multiValueHeaders?: {
|
|
265
|
+
[header: string]: Array<boolean | number | string>;
|
|
266
|
+
} | undefined;
|
|
267
|
+
body: string;
|
|
268
|
+
isBase64Encoded?: boolean | undefined;
|
|
269
|
+
}
|
|
270
|
+
//#endregion
|
|
271
|
+
//#region src/node/feedback/adapters/aws.d.ts
|
|
272
|
+
declare function handleAwsFeedback(event: APIGatewayProxyEvent, env?: Record<string, string | undefined>): Promise<APIGatewayProxyResult>;
|
|
273
|
+
//#endregion
|
|
274
|
+
export { handleVercelFeedback as i, handleWebFeedback as n, handleNetlifyFeedback as r, handleAwsFeedback as t };
|