boltdocs 2.7.9 → 2.7.11
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/{cache-DorPMFgW.cjs → cache-Ba-DZQNH.cjs} +1 -1
- package/dist/{cache-CQKlT4fI.mjs → cache-BuMZ58L5.mjs} +1 -1
- package/dist/chunk-CU-zTemE.cjs +6 -0
- package/dist/client/index.cjs +1929 -1
- package/dist/client/index.js +1880 -1
- package/dist/client/mdx.cjs +7 -1
- package/dist/client/mdx.js +7 -1
- package/dist/client/primitives.cjs +60 -1
- package/dist/client/primitives.js +20 -1
- package/dist/docs-layout-BXHV0xw_.cjs +1431 -0
- package/dist/docs-layout-DwFndmj5.js +1231 -0
- package/dist/doctor-Be7Ly1oM.mjs +21 -0
- package/dist/{doctor-D4_Y7M4p.cjs → doctor-CrytFkqW.cjs} +1 -1
- package/dist/doctor-jMxWZyLJ.cjs +21 -0
- package/dist/generator-CHqxiQhF.cjs +21 -0
- package/dist/generator-ClVanhvi.mjs +21 -0
- package/dist/icons-dev-3cZMyt8r.cjs +1204 -0
- package/dist/icons-dev-Df8OQ481.js +839 -0
- package/dist/image-DtrI2cw3.cjs +268 -0
- package/dist/image-jxPb-2iV.js +214 -0
- package/dist/mdx-BdWkJTeB.cjs +523 -0
- package/dist/mdx-UTTLFWJq.js +494 -0
- package/dist/meta-loader-CWg2gnbY.mjs +6 -0
- package/dist/meta-loader-Cv9O0Pzl.cjs +6 -0
- package/dist/node/cli-entry.cjs +1 -1
- package/dist/node/cli-entry.mjs +1 -1
- package/dist/node/index.cjs +1 -1
- 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-BSM4qcDK.cjs +111 -0
- package/dist/node-BspZN3R2.mjs +111 -0
- package/dist/{package-VfQM94VL.cjs → package-DIIrjuWI.cjs} +1 -1
- package/dist/{package-B4MD00N3.mjs → package-K0zsjGIz.mjs} +1 -1
- package/dist/{parser-Bh11BsdA.cjs → parser-Aq8LoH-0.cjs} +1 -1
- package/dist/{parser-DYRzXWmA.cjs → parser-CdNbqN5y.cjs} +1 -1
- package/dist/parser-nE792MLO.mjs +6 -0
- package/dist/rolldown-runtime-fkIsjY3S.mjs +6 -0
- package/dist/{routes-Co1mRM58.cjs → routes-2k3tbUmC.cjs} +1 -1
- package/dist/routes-CpxZIsMM.mjs +6 -0
- package/dist/{routes-CHf76Ye4.cjs → routes-DP1vmWRj.cjs} +1 -1
- package/dist/search-dialog-BHuIiUC6.js +8 -0
- package/dist/search-dialog-BNF10tDl.js +375 -0
- package/dist/search-dialog-BwkDuI9R.cjs +220 -0
- package/dist/search-dialog-C7xuvyNk.cjs +386 -0
- package/dist/search-dialog-CIQg6k8c.cjs +8 -0
- package/dist/search-dialog-D-DDN7zJ.js +208 -0
- package/dist/utils-CG65J0Sc.mjs +7 -0
- package/dist/utils-CKunkU96.cjs +7 -0
- package/dist/{worker-pool-BwU8ckrg.cjs → worker-pool-Crbqgw5R.cjs} +1 -1
- package/package.json +5 -5
- package/dist/chunk-Ds5LZdWN.cjs +0 -6
- package/dist/docs-layout-KoWNZc8_.js +0 -6
- package/dist/docs-layout-x2yKt2cL.cjs +0 -6
- package/dist/doctor-BD1BSB03.mjs +0 -23
- package/dist/doctor-BHc9ua6r.cjs +0 -23
- package/dist/generator-DGW6pkCC.cjs +0 -22
- package/dist/generator-Dv3wEmhZ.mjs +0 -22
- package/dist/icons-dev-B_RZIyxu.js +0 -6
- package/dist/icons-dev-BlV3wWFT.cjs +0 -6
- package/dist/image-BHhTvQzr.cjs +0 -6
- package/dist/image-CqKzYD8f.js +0 -6
- package/dist/mdx-DudBEac0.js +0 -7
- package/dist/mdx-r4cDQxWu.cjs +0 -7
- package/dist/meta-loader-0gJ4PtBC.cjs +0 -6
- package/dist/meta-loader-9IpAHWDS.mjs +0 -6
- package/dist/node-DBaH7kat.mjs +0 -111
- package/dist/node-t5C3Q85p.cjs +0 -111
- package/dist/parser-9cVdK7w9.mjs +0 -6
- package/dist/routes-DwrMa5-z.mjs +0 -6
- package/dist/search-dialog-B584t9ZF.js +0 -6
- package/dist/search-dialog-BvBopRsZ.cjs +0 -6
- package/dist/search-dialog-ByvGScjt.js +0 -6
- package/dist/search-dialog-Cyko6TJm.cjs +0 -6
- package/dist/search-dialog-D6BNohIJ.js +0 -6
- package/dist/search-dialog-DuYTIefy.cjs +0 -6
- package/dist/utils-BxNAXhZZ.mjs +0 -7
- package/dist/utils-Clzu7jvb.cjs +0 -7
- package/src/client/app/config-context.tsx +0 -51
- package/src/client/app/doc-page.tsx +0 -38
- package/src/client/app/docs-layout.tsx +0 -28
- package/src/client/app/head.tsx +0 -122
- package/src/client/app/helmet-compat.tsx +0 -36
- package/src/client/app/mdx-component.tsx +0 -8
- package/src/client/app/mdx-components-context.tsx +0 -72
- package/src/client/app/routes-context.tsx +0 -34
- package/src/client/app/scroll-handler.tsx +0 -74
- package/src/client/app/theme-context.tsx +0 -103
- package/src/client/app/ui-context.tsx +0 -42
- package/src/client/components/docs-layout-default.tsx +0 -85
- package/src/client/components/icons-dev.tsx +0 -282
- package/src/client/components/mdx/callout.tsx +0 -97
- package/src/client/components/mdx/card.tsx +0 -99
- package/src/client/components/mdx/cards.tsx +0 -27
- package/src/client/components/mdx/code-block.tsx +0 -184
- package/src/client/components/mdx/field.tsx +0 -33
- package/src/client/components/mdx/image.tsx +0 -44
- package/src/client/components/mdx/index.ts +0 -19
- package/src/client/components/mdx/table.tsx +0 -54
- package/src/client/components/mdx/typographics.tsx +0 -120
- package/src/client/components/mdx/use-code-block.ts +0 -34
- package/src/client/components/primitives/breadcrumbs.tsx +0 -54
- package/src/client/components/primitives/button-group.tsx +0 -54
- package/src/client/components/primitives/button.tsx +0 -6
- package/src/client/components/primitives/code-block.tsx +0 -120
- package/src/client/components/primitives/docs-layout.tsx +0 -125
- package/src/client/components/primitives/error-boundary.tsx +0 -107
- package/src/client/components/primitives/heading.tsx +0 -128
- package/src/client/components/primitives/helpers/observer.ts +0 -141
- package/src/client/components/primitives/image.tsx +0 -26
- package/src/client/components/primitives/link.tsx +0 -102
- package/src/client/components/primitives/menu.tsx +0 -137
- package/src/client/components/primitives/navbar.tsx +0 -466
- package/src/client/components/primitives/on-this-page.tsx +0 -430
- package/src/client/components/primitives/page-nav.tsx +0 -51
- package/src/client/components/primitives/popover.tsx +0 -28
- package/src/client/components/primitives/search-dialog.tsx +0 -193
- package/src/client/components/primitives/sidebar.tsx +0 -423
- package/src/client/components/primitives/skeleton.tsx +0 -26
- package/src/client/components/primitives/tabs.tsx +0 -70
- package/src/client/components/primitives/tooltip.tsx +0 -81
- package/src/client/components/primitives/types.ts +0 -11
- package/src/client/components/ui-base/banner.tsx +0 -66
- package/src/client/components/ui-base/breadcrumbs.tsx +0 -44
- package/src/client/components/ui-base/copy-markdown.tsx +0 -107
- package/src/client/components/ui-base/error-boundary.tsx +0 -15
- package/src/client/components/ui-base/github-stars.tsx +0 -29
- package/src/client/components/ui-base/icons.tsx +0 -240
- package/src/client/components/ui-base/index.ts +0 -16
- package/src/client/components/ui-base/last-updated.tsx +0 -27
- package/src/client/components/ui-base/navbar.tsx +0 -266
- package/src/client/components/ui-base/not-found.tsx +0 -26
- package/src/client/components/ui-base/on-this-page.tsx +0 -57
- package/src/client/components/ui-base/page-nav.tsx +0 -50
- package/src/client/components/ui-base/search-dialog.tsx +0 -163
- package/src/client/components/ui-base/search-highlight.tsx +0 -10
- package/src/client/components/ui-base/sidebar.tsx +0 -92
- package/src/client/components/ui-base/tabs.tsx +0 -83
- package/src/client/components/ui-base/theme-toggle.tsx +0 -130
- package/src/client/components/ui-base/version-i18n.tsx +0 -80
- package/src/client/hooks/index.ts +0 -13
- package/src/client/hooks/use-analytics.ts +0 -272
- package/src/client/hooks/use-breadcrumbs.ts +0 -22
- package/src/client/hooks/use-i18n.ts +0 -182
- package/src/client/hooks/use-localized-to.ts +0 -113
- package/src/client/hooks/use-location.ts +0 -5
- package/src/client/hooks/use-navbar.ts +0 -130
- package/src/client/hooks/use-page-nav.ts +0 -46
- package/src/client/hooks/use-routes.ts +0 -108
- package/src/client/hooks/use-search-highlight.ts +0 -185
- package/src/client/hooks/use-search.ts +0 -118
- package/src/client/hooks/use-sidebar.ts +0 -205
- package/src/client/hooks/use-tabs.ts +0 -46
- package/src/client/hooks/use-version.ts +0 -111
- package/src/client/index.ts +0 -31
- package/src/client/mdx.ts +0 -2
- package/src/client/primitives.ts +0 -19
- package/src/client/ssg/boltdocs-shell.tsx +0 -148
- package/src/client/ssg/create-routes.tsx +0 -473
- package/src/client/ssg/index.ts +0 -4
- package/src/client/ssg/mdx-page.tsx +0 -38
- package/src/client/store/boltdocs-context.tsx +0 -137
- package/src/client/theme/neutral.css +0 -141
- package/src/client/theme/reset.css +0 -189
- package/src/client/types.ts +0 -116
- package/src/client/utils/cn.ts +0 -6
- package/src/client/utils/copy-clipboard.ts +0 -22
- package/src/client/utils/get-base-file-path.ts +0 -21
- package/src/client/utils/github.ts +0 -121
- package/src/client/utils/i18n.ts +0 -23
- package/src/client/utils/path.ts +0 -9
- package/src/client/utils/react-to-text.ts +0 -34
- package/src/client/virtual.d.ts +0 -24
- /package/dist/{worker-pool-Bd8Y9KDv.mjs → worker-pool-CGn7DrLb.mjs} +0 -0
package/dist/utils-BxNAXhZZ.mjs
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Boltdocs - https://boltdocs.vercel.app
|
|
3
|
-
* Copyright (c) 2026 Jesus Alcala
|
|
4
|
-
* Licensed under the MIT License.
|
|
5
|
-
*/
|
|
6
|
-
import e from"fs";import t from"isomorphic-dompurify";import{z as n}from"zod";const r=/^[a-zA-Z0-9\-_/.()]+$/,i=n.looseObject({title:n.string().max(200).optional(),description:n.string().max(500).optional(),permalink:n.string().optional(),sidebarPosition:n.number().optional(),sidebarLabel:n.string().max(100).optional(),sidebarHidden:n.boolean().optional(),hidden:n.boolean().optional(),category:n.string().max(50).optional(),order:n.number().optional(),badge:n.union([n.string().max(50),n.object({text:n.string().max(50),expires:n.string().optional()})]).optional(),icon:n.string().max(50).optional(),date:n.union([n.string(),n.date()]).optional(),lastUpdated:n.union([n.string(),n.date()]).optional(),groupTitle:n.string().max(100).optional(),groupPosition:n.number().optional(),seo:n.record(n.any()).optional()});function a(e){let t=e.trim();if(!t.startsWith(`---`))return{data:{},content:e,rawMatter:``};let n=t.indexOf(`---`,3);if(n===-1||n===3)return{data:{},content:e,rawMatter:``};let r=t.slice(3,n).trim(),i=t.slice(n+3).trim();return o(r)?{data:{},content:i,rawMatter:r}:{data:s(r),content:i,rawMatter:r}}function o(e){let t=!1,n=!1;for(let r=0;r<e.length;r++){let i=e[r],a=r>0?e[r-1]:``;i===`'`&&!n&&a!==`\\`?t=!t:i===`"`&&!t&&a!==`\\`&&(n=!n)}return t||n}function s(e){let t=e.split(`
|
|
7
|
-
`),n={},r=0;for(;r<t.length;){let e=t[r],i=e.trim();if(!i||i.startsWith(`#`)){r++;continue}let a=i.indexOf(`:`);if(a!==-1){let o=i.slice(0,a).trim(),s=i.slice(a+1).trim();if(s===``){let i=c(t.slice(r+1),e.search(/\S|$/));i.value===void 0?r++:(n[o]=i.value,r+=i.linesConsumed)}else n[o]=l(s),r++}else r++}return n}function c(e,t){if(e.length===0)return{value:void 0,linesConsumed:0};if(e[0].trim().startsWith(`-`)){let n=[],r=0;for(;r<e.length;){let i=e[r],a=i.search(/\S|$/),o=i.trim();if(!o||o.startsWith(`#`)){r++;continue}if(o.startsWith(`-`)){let e=o.slice(1).trim();if(e.includes(`:`)){let t={},r=e.split(/,\s*/);for(let e of r){let n=e.indexOf(`:`);if(n!==-1){let r=e.slice(0,n).trim();t[r]=l(e.slice(n+1).trim())}}n.push(t)}else n.push(e)}else if(a>t&&n.length>0){let e=n[n.length-1];if(typeof e==`object`&&e){let t=e,n=o.indexOf(`:`);if(n!==-1){let e=o.slice(0,n).trim();t[e]=l(o.slice(n+1).trim())}}}else break;r++}return{value:n.length>0?n:void 0,linesConsumed:r}}let n={},r=0;for(;r<e.length;){let i=e[r],a=i.search(/\S|$/),o=i.trim();if(!o||o.startsWith(`#`)){r++;continue}if(a<=t)break;let s=o.indexOf(`:`);if(s!==-1){let e=o.slice(0,s).trim();n[e]=l(o.slice(s+1).trim())}r++}return{value:Object.keys(n).length>0?n:void 0,linesConsumed:r}}function l(e){let t=e.trim();if(t===`true`)return!0;if(t===`false`)return!1;if(t===`null`||t===`~`)return null;if(/^-?\d+(\.\d+)?$/.test(t))return Number(t);if(t.startsWith(`"`)&&t.endsWith(`"`)||t.startsWith(`'`)&&t.endsWith(`'`))return t.slice(1,-1);if(t.startsWith(`{`)&&t.endsWith(`}`)){let e=t.slice(1,-1).trim(),n={},r=e.split(/,\s*/);for(let e of r){let t=e.indexOf(`:`);if(t!==-1){let r=e.slice(0,t).trim();n[r]=l(e.slice(t+1).trim())}}return n}return t}var u=class e extends Error{constructor(t){super(t),this.name=`SecurityViolationError`,Object.setPrototypeOf(this,e.prototype)}},d=class e extends u{constructor(t){super(t),this.name=`PathTraversalError`,Object.setPrototypeOf(this,e.prototype)}},f=class e extends u{constructor(t){super(t),this.name=`EncodingSecurityError`,Object.setPrototypeOf(this,e.prototype)}},p=class e extends u{constructor(t){super(t),this.name=`ValidationError`,Object.setPrototypeOf(this,e.prototype)}};function m(e){return e.replace(/\\/g,`/`)}function h(e){return e.replace(/^\d+\./,``)}function g(e){let t=e.match(/^(\d+)\./);return t?parseInt(t[1],10):void 0}function _(e){return/\.mdx?$/.test(e)}function v(t){try{return e.statSync(t).mtimeMs}catch{return 0}}async function y(t,n=!0){let r=``;try{r=await e.promises.readFile(t,`utf-8`);let{data:o,content:s,rawMatter:c}=a(r);if(c&&c.length>10240)throw D(`FRONTMATTER_TOO_LARGE`,`Frontmatter block exceeds size limit`,{size:c.length,file:t}),new p(`Security breach: Frontmatter size exceeds limit of 10240 bytes`);if(!n)return{data:o,content:s,raw:r};let l=i.safeParse(o),u={...l.success?l.data:{}};return u.lastUpdated||=(await e.promises.stat(t)).mtimeMs,u.title&&=w(u.title).trim(),u.description&&=w(u.description).trim(),{data:u,content:s,raw:r}}catch(e){if(e instanceof p)throw e;return{data:{},content:r,raw:r}}}function b(e){return e.replace(/&/g,`&`).replace(/"/g,`"`).replace(/'/g,`'`).replace(/</g,`<`).replace(/>/g,`>`)}function x(e){return b(e)}function S(e){let t=m(e).split(`/`).map(e=>E(h(e))).join(`/`).replace(/\/$/,``);return t=t.replace(/\.mdx?$/,``),(t===`index`||t.endsWith(`/index`))&&(t=t.replace(/index$/,``)),t.startsWith(`/`)||(t=`/`+t),t.length>1&&t.endsWith(`/`)&&(t=t.slice(0,-1)),t}function C(e){return t.sanitize(e,{ALLOWED_TAGS:`b.i.em.strong.a.p.br.code.pre.span.div.h1.h2.h3.h4.h5.h6.ul.ol.li.table.thead.tbody.tr.th.td.blockquote.hr`.split(`.`),ALLOWED_ATTR:[`href`,`title`,`target`,`class`,`id`,`src`,`alt`,`width`,`height`],FORCE_BODY:!0})}t.addHook(`afterSanitizeAttributes`,e=>{if(e.hasAttribute(`href`)){let t=e.getAttribute(`href`)?.toLowerCase()||``;(t.startsWith(`javascript:`)||t.startsWith(`data:`)||t.startsWith(`vbscript:`))&&e.removeAttribute(`href`)}if(e.hasAttribute(`src`)){let t=e.getAttribute(`src`)?.toLowerCase()||``;(t.startsWith(`javascript:`)||t.startsWith(`data:`)||t.startsWith(`vbscript:`))&&e.removeAttribute(`src`)}});function w(e){return e?e.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,``).replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi,``).replace(/<!--[\s\S]*?-->/g,``).replace(/<[^>]+>/g,` `).replace(/\s+/g,` `).trim():``}function T(e){return e.charAt(0).toUpperCase()+e.slice(1)}function E(e){return e.replace(/[^a-zA-Z0-9\-_/.]/g,``).split(`/`).filter(e=>e!==`..`&&e!==`.`).map(e=>e.replace(/\.\.+/g,`.`)).join(`/`)}function D(e,t,n={}){let r=new Date().toISOString(),i={...n};for(let e in i)typeof i[e]==`string`&&i[e].includes(`:`)&&(i[e]=i[e].split(/[\\/]/).pop()||i[e]);console.error(`[SECURITY][${r}] TYPE: ${e} | MESSAGE: ${t} | DETAILS: ${JSON.stringify(i)}`)}function O(){return{dir:process.env.BOLTDOCS_CACHE_DIR||`.boltdocs`,noCache:process.env.BOLTDOCS_NO_CACHE===`1`,lruLimit:parseInt(process.env.BOLTDOCS_CACHE_LRU_LIMIT||`2000`,10),lruTTL:parseInt(process.env.BOLTDOCS_CACHE_LRU_TTL||`14400000`,10),compress:process.env.BOLTDOCS_CACHE_COMPRESS!==`0`}}export{p as _,O as a,D as c,E as d,C as f,d as g,f as h,S as i,m as l,h as m,x as n,v as o,w as p,g as r,_ as s,T as t,y as u,i as v,r as y};
|
package/dist/utils-Clzu7jvb.cjs
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
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`);let t=require(`fs`);t=e.n(t);let n=require(`isomorphic-dompurify`);n=e.n(n);let r=require(`zod`);const i=/^[a-zA-Z0-9\-_/.()]+$/,a=r.z.looseObject({title:r.z.string().max(200).optional(),description:r.z.string().max(500).optional(),permalink:r.z.string().optional(),sidebarPosition:r.z.number().optional(),sidebarLabel:r.z.string().max(100).optional(),sidebarHidden:r.z.boolean().optional(),hidden:r.z.boolean().optional(),category:r.z.string().max(50).optional(),order:r.z.number().optional(),badge:r.z.union([r.z.string().max(50),r.z.object({text:r.z.string().max(50),expires:r.z.string().optional()})]).optional(),icon:r.z.string().max(50).optional(),date:r.z.union([r.z.string(),r.z.date()]).optional(),lastUpdated:r.z.union([r.z.string(),r.z.date()]).optional(),groupTitle:r.z.string().max(100).optional(),groupPosition:r.z.number().optional(),seo:r.z.record(r.z.any()).optional()});function o(e){let t=e.trim();if(!t.startsWith(`---`))return{data:{},content:e,rawMatter:``};let n=t.indexOf(`---`,3);if(n===-1||n===3)return{data:{},content:e,rawMatter:``};let r=t.slice(3,n).trim(),i=t.slice(n+3).trim();return s(r)?{data:{},content:i,rawMatter:r}:{data:c(r),content:i,rawMatter:r}}function s(e){let t=!1,n=!1;for(let r=0;r<e.length;r++){let i=e[r],a=r>0?e[r-1]:``;i===`'`&&!n&&a!==`\\`?t=!t:i===`"`&&!t&&a!==`\\`&&(n=!n)}return t||n}function c(e){let t=e.split(`
|
|
7
|
-
`),n={},r=0;for(;r<t.length;){let e=t[r],i=e.trim();if(!i||i.startsWith(`#`)){r++;continue}let a=i.indexOf(`:`);if(a!==-1){let o=i.slice(0,a).trim(),s=i.slice(a+1).trim();if(s===``){let i=l(t.slice(r+1),e.search(/\S|$/));i.value===void 0?r++:(n[o]=i.value,r+=i.linesConsumed)}else n[o]=u(s),r++}else r++}return n}function l(e,t){if(e.length===0)return{value:void 0,linesConsumed:0};if(e[0].trim().startsWith(`-`)){let n=[],r=0;for(;r<e.length;){let i=e[r],a=i.search(/\S|$/),o=i.trim();if(!o||o.startsWith(`#`)){r++;continue}if(o.startsWith(`-`)){let e=o.slice(1).trim();if(e.includes(`:`)){let t={},r=e.split(/,\s*/);for(let e of r){let n=e.indexOf(`:`);if(n!==-1){let r=e.slice(0,n).trim();t[r]=u(e.slice(n+1).trim())}}n.push(t)}else n.push(e)}else if(a>t&&n.length>0){let e=n[n.length-1];if(typeof e==`object`&&e){let t=e,n=o.indexOf(`:`);if(n!==-1){let e=o.slice(0,n).trim();t[e]=u(o.slice(n+1).trim())}}}else break;r++}return{value:n.length>0?n:void 0,linesConsumed:r}}let n={},r=0;for(;r<e.length;){let i=e[r],a=i.search(/\S|$/),o=i.trim();if(!o||o.startsWith(`#`)){r++;continue}if(a<=t)break;let s=o.indexOf(`:`);if(s!==-1){let e=o.slice(0,s).trim();n[e]=u(o.slice(s+1).trim())}r++}return{value:Object.keys(n).length>0?n:void 0,linesConsumed:r}}function u(e){let t=e.trim();if(t===`true`)return!0;if(t===`false`)return!1;if(t===`null`||t===`~`)return null;if(/^-?\d+(\.\d+)?$/.test(t))return Number(t);if(t.startsWith(`"`)&&t.endsWith(`"`)||t.startsWith(`'`)&&t.endsWith(`'`))return t.slice(1,-1);if(t.startsWith(`{`)&&t.endsWith(`}`)){let e=t.slice(1,-1).trim(),n={},r=e.split(/,\s*/);for(let e of r){let t=e.indexOf(`:`);if(t!==-1){let r=e.slice(0,t).trim();n[r]=u(e.slice(t+1).trim())}}return n}return t}var d=class e extends Error{constructor(t){super(t),this.name=`SecurityViolationError`,Object.setPrototypeOf(this,e.prototype)}},f=class e extends d{constructor(t){super(t),this.name=`PathTraversalError`,Object.setPrototypeOf(this,e.prototype)}},p=class e extends d{constructor(t){super(t),this.name=`EncodingSecurityError`,Object.setPrototypeOf(this,e.prototype)}},m=class e extends d{constructor(t){super(t),this.name=`ValidationError`,Object.setPrototypeOf(this,e.prototype)}};function h(e){return e.replace(/\\/g,`/`)}function g(e){return e.replace(/^\d+\./,``)}function _(e){let t=e.match(/^(\d+)\./);return t?parseInt(t[1],10):void 0}function v(e){return/\.mdx?$/.test(e)}function y(e){try{return t.default.statSync(e).mtimeMs}catch{return 0}}async function b(e,n=!0){let r=``;try{r=await t.default.promises.readFile(e,`utf-8`);let{data:i,content:s,rawMatter:c}=o(r);if(c&&c.length>10240)throw O(`FRONTMATTER_TOO_LARGE`,`Frontmatter block exceeds size limit`,{size:c.length,file:e}),new m(`Security breach: Frontmatter size exceeds limit of 10240 bytes`);if(!n)return{data:i,content:s,raw:r};let l=a.safeParse(i),u={...l.success?l.data:{}};return u.lastUpdated||=(await t.default.promises.stat(e)).mtimeMs,u.title&&=T(u.title).trim(),u.description&&=T(u.description).trim(),{data:u,content:s,raw:r}}catch(e){if(e instanceof m)throw e;return{data:{},content:r,raw:r}}}function x(e){return e.replace(/&/g,`&`).replace(/"/g,`"`).replace(/'/g,`'`).replace(/</g,`<`).replace(/>/g,`>`)}function S(e){return x(e)}function C(e){let t=h(e).split(`/`).map(e=>D(g(e))).join(`/`).replace(/\/$/,``);return t=t.replace(/\.mdx?$/,``),(t===`index`||t.endsWith(`/index`))&&(t=t.replace(/index$/,``)),t.startsWith(`/`)||(t=`/`+t),t.length>1&&t.endsWith(`/`)&&(t=t.slice(0,-1)),t}function w(e){return n.default.sanitize(e,{ALLOWED_TAGS:`b.i.em.strong.a.p.br.code.pre.span.div.h1.h2.h3.h4.h5.h6.ul.ol.li.table.thead.tbody.tr.th.td.blockquote.hr`.split(`.`),ALLOWED_ATTR:[`href`,`title`,`target`,`class`,`id`,`src`,`alt`,`width`,`height`],FORCE_BODY:!0})}n.default.addHook(`afterSanitizeAttributes`,e=>{if(e.hasAttribute(`href`)){let t=e.getAttribute(`href`)?.toLowerCase()||``;(t.startsWith(`javascript:`)||t.startsWith(`data:`)||t.startsWith(`vbscript:`))&&e.removeAttribute(`href`)}if(e.hasAttribute(`src`)){let t=e.getAttribute(`src`)?.toLowerCase()||``;(t.startsWith(`javascript:`)||t.startsWith(`data:`)||t.startsWith(`vbscript:`))&&e.removeAttribute(`src`)}});function T(e){return e?e.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,``).replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi,``).replace(/<!--[\s\S]*?-->/g,``).replace(/<[^>]+>/g,` `).replace(/\s+/g,` `).trim():``}function E(e){return e.charAt(0).toUpperCase()+e.slice(1)}function D(e){return e.replace(/[^a-zA-Z0-9\-_/.]/g,``).split(`/`).filter(e=>e!==`..`&&e!==`.`).map(e=>e.replace(/\.\.+/g,`.`)).join(`/`)}function O(e,t,n={}){let r=new Date().toISOString(),i={...n};for(let e in i)typeof i[e]==`string`&&i[e].includes(`:`)&&(i[e]=i[e].split(/[\\/]/).pop()||i[e]);console.error(`[SECURITY][${r}] TYPE: ${e} | MESSAGE: ${t} | DETAILS: ${JSON.stringify(i)}`)}function k(){return{dir:process.env.BOLTDOCS_CACHE_DIR||`.boltdocs`,noCache:process.env.BOLTDOCS_NO_CACHE===`1`,lruLimit:parseInt(process.env.BOLTDOCS_CACHE_LRU_LIMIT||`2000`,10),lruTTL:parseInt(process.env.BOLTDOCS_CACHE_LRU_TTL||`14400000`,10),compress:process.env.BOLTDOCS_CACHE_COMPRESS!==`0`}}Object.defineProperty(exports,`_`,{enumerable:!0,get:function(){return m}}),Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return k}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return O}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return D}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,`g`,{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,`h`,{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return h}}),Object.defineProperty(exports,`m`,{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return S}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,`p`,{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return _}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return v}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return E}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return b}}),Object.defineProperty(exports,`v`,{enumerable:!0,get:function(){return a}}),Object.defineProperty(exports,`y`,{enumerable:!0,get:function(){return i}});
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { createContext, use } from 'react'
|
|
2
|
-
import type { BoltdocsConfig } from '../../shared/types'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Context for the global documentation configuration.
|
|
6
|
-
* Using a global singleton pattern to survive dual-package or duplicated-code hazards.
|
|
7
|
-
*/
|
|
8
|
-
const CONFIG_CONTEXT_SYMBOL = Symbol.for('__BDOCS_CONFIG_CONTEXT__')
|
|
9
|
-
const CONFIG_INSTANCE_SYMBOL = Symbol.for('__BDOCS_CONFIG_INSTANCE__')
|
|
10
|
-
|
|
11
|
-
export const ConfigContext =
|
|
12
|
-
(globalThis as any)[CONFIG_CONTEXT_SYMBOL] ||
|
|
13
|
-
((globalThis as any)[CONFIG_CONTEXT_SYMBOL] =
|
|
14
|
-
createContext<BoltdocsConfig | null>(null))
|
|
15
|
-
|
|
16
|
-
export function ConfigProvider({
|
|
17
|
-
config,
|
|
18
|
-
children,
|
|
19
|
-
}: {
|
|
20
|
-
config: BoltdocsConfig
|
|
21
|
-
children: React.ReactNode
|
|
22
|
-
}) {
|
|
23
|
-
// Sync with global registry for dual-package fallback
|
|
24
|
-
if (typeof globalThis !== 'undefined') {
|
|
25
|
-
;(globalThis as any)[CONFIG_INSTANCE_SYMBOL] = config
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<ConfigContext.Provider value={config}>{children}</ConfigContext.Provider>
|
|
30
|
-
)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Hook to access the Boltdocs configuration.
|
|
35
|
-
*/
|
|
36
|
-
export function useConfig() {
|
|
37
|
-
const context = use(ConfigContext)
|
|
38
|
-
|
|
39
|
-
// Fallback to global registry if context is missing (dual-package hazard safety net)
|
|
40
|
-
if (
|
|
41
|
-
!context &&
|
|
42
|
-
typeof globalThis !== 'undefined' &&
|
|
43
|
-
(globalThis as any)[CONFIG_INSTANCE_SYMBOL]
|
|
44
|
-
) {
|
|
45
|
-
return (globalThis as any)[CONFIG_INSTANCE_SYMBOL] as BoltdocsConfig
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (!context)
|
|
49
|
-
throw new Error('useConfig must be used within a ConfigProvider')
|
|
50
|
-
return context as BoltdocsConfig
|
|
51
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { useMdxComponents } from './mdx-components-context'
|
|
2
|
-
import { useMemo } from 'react'
|
|
3
|
-
import { LastUpdated as DefaultLastUpdated } from '../components/ui-base'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* DocPage renders the MDX content and page-specific metadata.
|
|
7
|
-
* It is rendered inside the Outlet of DocsLayout.
|
|
8
|
-
*/
|
|
9
|
-
export function DocPage({
|
|
10
|
-
route,
|
|
11
|
-
content: Content,
|
|
12
|
-
mdxComponents: propComponents,
|
|
13
|
-
}: any) {
|
|
14
|
-
// Access global MDX components (defaults + plugins + virtuals) from context
|
|
15
|
-
const contextComponents = useMdxComponents()
|
|
16
|
-
|
|
17
|
-
// Merge components: Prop components (from loader) take priority,
|
|
18
|
-
// then context components (globals).
|
|
19
|
-
const allComponents = useMemo(
|
|
20
|
-
() => ({
|
|
21
|
-
LastUpdated: DefaultLastUpdated,
|
|
22
|
-
...contextComponents,
|
|
23
|
-
...propComponents,
|
|
24
|
-
}),
|
|
25
|
-
[contextComponents, propComponents],
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
const LastUpdated = allComponents.LastUpdated || DefaultLastUpdated
|
|
29
|
-
|
|
30
|
-
if (!Content) return null
|
|
31
|
-
|
|
32
|
-
return (
|
|
33
|
-
<>
|
|
34
|
-
<Content components={allComponents} />
|
|
35
|
-
{route?.lastUpdated && <LastUpdated date={route.lastUpdated} />}
|
|
36
|
-
</>
|
|
37
|
-
)
|
|
38
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { Outlet } from 'react-router-dom'
|
|
2
|
-
import UserLayout from 'virtual:boltdocs-layout'
|
|
3
|
-
import { useRoutes } from '../hooks/use-routes'
|
|
4
|
-
import { useConfig } from './config-context'
|
|
5
|
-
import { Head } from './head'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Wraps the docs Outlet with the user's (or default) layout component.
|
|
9
|
-
* The Layout receives the routed page as `children`.
|
|
10
|
-
* We use useRoutes to pass the current route context to the persistent layout.
|
|
11
|
-
*/
|
|
12
|
-
export function DocsLayout() {
|
|
13
|
-
const config = useConfig()
|
|
14
|
-
const { currentRoute, allRoutes } = useRoutes()
|
|
15
|
-
|
|
16
|
-
return (
|
|
17
|
-
<>
|
|
18
|
-
<Head
|
|
19
|
-
siteTitle={config.theme?.title}
|
|
20
|
-
siteDescription={config.theme?.description}
|
|
21
|
-
routes={allRoutes || []}
|
|
22
|
-
/>
|
|
23
|
-
<UserLayout route={currentRoute}>
|
|
24
|
-
<Outlet />
|
|
25
|
-
</UserLayout>
|
|
26
|
-
</>
|
|
27
|
-
)
|
|
28
|
-
}
|
package/src/client/app/head.tsx
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import { useMemo } from 'react'
|
|
2
|
-
import { useLocation } from 'react-router-dom'
|
|
3
|
-
import { Helmet } from './helmet-compat'
|
|
4
|
-
import { useConfig } from './config-context'
|
|
5
|
-
import { getTranslated } from '../utils/i18n'
|
|
6
|
-
import { useRoutes } from '../hooks/use-routes'
|
|
7
|
-
|
|
8
|
-
interface HeadProps {
|
|
9
|
-
siteTitle?: string | Record<string, string>
|
|
10
|
-
siteDescription?: string | Record<string, string>
|
|
11
|
-
routes: Array<{
|
|
12
|
-
path: string
|
|
13
|
-
title: string
|
|
14
|
-
description?: string
|
|
15
|
-
seo?: Record<string, unknown>
|
|
16
|
-
}>
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function Head({ siteTitle, siteDescription, routes }: HeadProps) {
|
|
20
|
-
const location = useLocation()
|
|
21
|
-
const config = useConfig()
|
|
22
|
-
const { currentLocale } = useRoutes()
|
|
23
|
-
|
|
24
|
-
// Find the current route's metadata — memoized so the O(n) search only
|
|
25
|
-
// re-runs when the routes array or the current URL changes, not on every render.
|
|
26
|
-
const currentRoute = useMemo(
|
|
27
|
-
() => routes?.find?.((r) => r.path === location.pathname),
|
|
28
|
-
[routes, location.pathname],
|
|
29
|
-
)
|
|
30
|
-
const pageTitle = currentRoute?.title
|
|
31
|
-
const translatedSiteDescription = getTranslated(
|
|
32
|
-
siteDescription,
|
|
33
|
-
currentLocale,
|
|
34
|
-
)
|
|
35
|
-
const pageDescription =
|
|
36
|
-
currentRoute?.description || translatedSiteDescription || ''
|
|
37
|
-
|
|
38
|
-
const translatedSiteTitle = getTranslated(siteTitle, currentLocale)
|
|
39
|
-
const finalTitle = pageTitle
|
|
40
|
-
? `${pageTitle} | ${translatedSiteTitle}`
|
|
41
|
-
: translatedSiteTitle
|
|
42
|
-
|
|
43
|
-
const seo = currentRoute?.seo || {}
|
|
44
|
-
|
|
45
|
-
// Merge custom global metatags
|
|
46
|
-
const globalMetatags = config?.seo?.metatags || {}
|
|
47
|
-
|
|
48
|
-
// Calculate specific ones
|
|
49
|
-
const defaultOgImage = config?.seo?.thumbnails?.background
|
|
50
|
-
const ogImage = (seo['og:image'] || defaultOgImage) as string | undefined
|
|
51
|
-
|
|
52
|
-
return (
|
|
53
|
-
<Helmet>
|
|
54
|
-
<title>{finalTitle}</title>
|
|
55
|
-
<meta name="description" content={pageDescription} />
|
|
56
|
-
|
|
57
|
-
{/* Default OG Tags */}
|
|
58
|
-
<meta property="og:title" content={finalTitle} />
|
|
59
|
-
<meta property="og:description" content={pageDescription} />
|
|
60
|
-
<meta property="og:type" content="article" />
|
|
61
|
-
{/* Canonical URL for both <link> and og:url */}
|
|
62
|
-
{typeof window !== 'undefined' && (
|
|
63
|
-
<meta property="og:url" content={window.location.href} />
|
|
64
|
-
)}
|
|
65
|
-
{typeof window !== 'undefined' && (
|
|
66
|
-
<link
|
|
67
|
-
rel="canonical"
|
|
68
|
-
href={window.location.origin + location.pathname}
|
|
69
|
-
/>
|
|
70
|
-
)}
|
|
71
|
-
|
|
72
|
-
{/* Default Twitter Card */}
|
|
73
|
-
<meta name="twitter:card" content="summary" />
|
|
74
|
-
<meta name="twitter:title" content={finalTitle} />
|
|
75
|
-
<meta name="twitter:description" content={pageDescription} />
|
|
76
|
-
{ogImage && <meta name="twitter:image" content={ogImage} />}
|
|
77
|
-
{ogImage && <meta property="og:image" content={ogImage} />}
|
|
78
|
-
|
|
79
|
-
{/* Generator */}
|
|
80
|
-
<meta name="generator" content="Boltdocs" />
|
|
81
|
-
|
|
82
|
-
{/* User-defined global metatags */}
|
|
83
|
-
{Object.entries(globalMetatags).map(([key, value]) => {
|
|
84
|
-
const isProperty =
|
|
85
|
-
key.startsWith('og:') ||
|
|
86
|
-
key.startsWith('music:') ||
|
|
87
|
-
key.startsWith('video:') ||
|
|
88
|
-
key.startsWith('article:') ||
|
|
89
|
-
key.startsWith('book:') ||
|
|
90
|
-
key.startsWith('profile:')
|
|
91
|
-
return isProperty ? (
|
|
92
|
-
<meta key={key} property={key} content={value as string} />
|
|
93
|
-
) : (
|
|
94
|
-
<meta key={key} name={key} content={value as string} />
|
|
95
|
-
)
|
|
96
|
-
})}
|
|
97
|
-
|
|
98
|
-
{/* Page granular SEO tags (override global) */}
|
|
99
|
-
{Object.entries(seo).map(([key, value]) => {
|
|
100
|
-
if (key === 'noindex' && value === true)
|
|
101
|
-
return <meta key="noindex" name="robots" content="noindex" />
|
|
102
|
-
if (key === 'robots')
|
|
103
|
-
return <meta key="robots" name="robots" content={value as string} />
|
|
104
|
-
if (key === 'canonical')
|
|
105
|
-
return <link key="canonical" rel="canonical" href={value as string} />
|
|
106
|
-
|
|
107
|
-
const isProperty =
|
|
108
|
-
key.startsWith('og:') ||
|
|
109
|
-
key.startsWith('music:') ||
|
|
110
|
-
key.startsWith('video:') ||
|
|
111
|
-
key.startsWith('article:') ||
|
|
112
|
-
key.startsWith('book:') ||
|
|
113
|
-
key.startsWith('profile:')
|
|
114
|
-
return isProperty ? (
|
|
115
|
-
<meta key={key} property={key} content={value as string} />
|
|
116
|
-
) : (
|
|
117
|
-
<meta key={key} name={key} content={value as string} />
|
|
118
|
-
)
|
|
119
|
-
})}
|
|
120
|
-
</Helmet>
|
|
121
|
-
)
|
|
122
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared Helmet module compatibility helpers.
|
|
3
|
-
*
|
|
4
|
-
* react-helmet-async ships different module shapes depending on whether it is
|
|
5
|
-
* loaded via CJS or ESM. Instead of duplicating the same detection logic in
|
|
6
|
-
* every component that needs Helmet/HelmetProvider, we centralise it here.
|
|
7
|
-
*/
|
|
8
|
-
import type { ComponentType, ReactNode } from 'react'
|
|
9
|
-
import * as ReactHelmetAsync from 'react-helmet-async'
|
|
10
|
-
|
|
11
|
-
type HelmetModule = {
|
|
12
|
-
Helmet?: ComponentType<{ children?: ReactNode }>
|
|
13
|
-
HelmetProvider?: ComponentType<{ children?: ReactNode }>
|
|
14
|
-
default?: {
|
|
15
|
-
Helmet?: ComponentType<{ children?: ReactNode }>
|
|
16
|
-
HelmetProvider?: ComponentType<{ children?: ReactNode }>
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const mod = ReactHelmetAsync as unknown as HelmetModule
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* The `<Helmet>` component, resolved across CJS/ESM module shapes.
|
|
24
|
-
* Falls back to a transparent fragment wrapper if the module cannot be resolved.
|
|
25
|
-
*/
|
|
26
|
-
export const Helmet: ComponentType<{ children?: ReactNode }> =
|
|
27
|
-
mod.Helmet || mod.default?.Helmet || (({ children }) => <>{children}</>)
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* The `<HelmetProvider>` component, resolved across CJS/ESM module shapes.
|
|
31
|
-
* Falls back to a transparent fragment wrapper if the module cannot be resolved.
|
|
32
|
-
*/
|
|
33
|
-
export const HelmetProvider: ComponentType<{ children?: ReactNode }> =
|
|
34
|
-
mod.HelmetProvider ||
|
|
35
|
-
mod.default?.HelmetProvider ||
|
|
36
|
-
(({ children }) => <>{children}</>)
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { createContext, use, useMemo } from 'react'
|
|
2
|
-
import type { BoltdocsMdxComponents } from '../../shared/types'
|
|
3
|
-
|
|
4
|
-
export type MdxComponentsType = {
|
|
5
|
-
[key: string]: React.ComponentType<any>
|
|
6
|
-
} & {
|
|
7
|
-
Frontmatter?: Record<string, React.ComponentType<any>>
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const MDX_COMPONENTS_CONTEXT_SYMBOL = Symbol.for(
|
|
11
|
-
'__BDOCS_MDX_COMPONENTS_CONTEXT__',
|
|
12
|
-
)
|
|
13
|
-
const MDX_COMPONENTS_INSTANCE_SYMBOL = Symbol.for(
|
|
14
|
-
'__BDOCS_MDX_COMPONENTS_INSTANCE__',
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
const MdxComponentsContext =
|
|
18
|
-
(globalThis as any)[MDX_COMPONENTS_CONTEXT_SYMBOL] ||
|
|
19
|
-
((globalThis as any)[MDX_COMPONENTS_CONTEXT_SYMBOL] =
|
|
20
|
-
createContext<MdxComponentsType>({}))
|
|
21
|
-
|
|
22
|
-
export function useMdxComponents(): BoltdocsMdxComponents {
|
|
23
|
-
const context = use(MdxComponentsContext)
|
|
24
|
-
|
|
25
|
-
// Fallback to global registry for dual-package hazards
|
|
26
|
-
if (
|
|
27
|
-
(!context || Object.keys(context).length === 0) &&
|
|
28
|
-
(globalThis as any)[MDX_COMPONENTS_INSTANCE_SYMBOL]
|
|
29
|
-
) {
|
|
30
|
-
return (globalThis as any)[
|
|
31
|
-
MDX_COMPONENTS_INSTANCE_SYMBOL
|
|
32
|
-
] as BoltdocsMdxComponents
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return context as any as BoltdocsMdxComponents
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function MdxComponentsProvider({
|
|
39
|
-
components,
|
|
40
|
-
children,
|
|
41
|
-
}: {
|
|
42
|
-
components: Record<string, React.ComponentType<any>>
|
|
43
|
-
children: React.ReactNode
|
|
44
|
-
}) {
|
|
45
|
-
const processedComponents = useMemo(() => {
|
|
46
|
-
const processed: Record<string, any> = {}
|
|
47
|
-
const frontmatter: Record<string, React.ComponentType<any>> = {}
|
|
48
|
-
|
|
49
|
-
Object.entries(components).forEach(([key, value]) => {
|
|
50
|
-
if (key.startsWith('Frontmatter_')) {
|
|
51
|
-
const cleanKey = key.slice('Frontmatter_'.length)
|
|
52
|
-
frontmatter[cleanKey] = value
|
|
53
|
-
} else {
|
|
54
|
-
processed[key] = value
|
|
55
|
-
}
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
processed.Frontmatter = frontmatter
|
|
59
|
-
return processed as MdxComponentsType
|
|
60
|
-
}, [components])
|
|
61
|
-
|
|
62
|
-
// Sync with global registry
|
|
63
|
-
if (typeof globalThis !== 'undefined') {
|
|
64
|
-
;(globalThis as any)[MDX_COMPONENTS_INSTANCE_SYMBOL] = processedComponents
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return (
|
|
68
|
-
<MdxComponentsContext.Provider value={processedComponents}>
|
|
69
|
-
{children}
|
|
70
|
-
</MdxComponentsContext.Provider>
|
|
71
|
-
)
|
|
72
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { createContext, use } from 'react'
|
|
2
|
-
import type { ComponentRoute } from '../types'
|
|
3
|
-
|
|
4
|
-
interface RoutesContextType {
|
|
5
|
-
routes: ComponentRoute[]
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const RoutesContext = createContext<RoutesContextType>({
|
|
9
|
-
routes: [],
|
|
10
|
-
})
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Hook to access the processed routes list from the closest provider.
|
|
14
|
-
*/
|
|
15
|
-
export function useRoutesContext() {
|
|
16
|
-
return use(RoutesContext)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Provider component for the documentation routes.
|
|
21
|
-
*/
|
|
22
|
-
export function RoutesProvider({
|
|
23
|
-
routes,
|
|
24
|
-
children,
|
|
25
|
-
}: {
|
|
26
|
-
routes: ComponentRoute[]
|
|
27
|
-
children: React.ReactNode
|
|
28
|
-
}) {
|
|
29
|
-
return (
|
|
30
|
-
<RoutesContext.Provider value={{ routes }}>
|
|
31
|
-
{children}
|
|
32
|
-
</RoutesContext.Provider>
|
|
33
|
-
)
|
|
34
|
-
}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { useEffect, useLayoutEffect } from 'react'
|
|
2
|
-
import { useLocation } from 'react-router-dom'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Handles scroll restoration and hash scrolling on navigation.
|
|
6
|
-
* It ensures the page scrolls to top on pathname changes,
|
|
7
|
-
* or specifically to an anchor element if a hash is present.
|
|
8
|
-
*/
|
|
9
|
-
export function ScrollHandler() {
|
|
10
|
-
const { pathname, hash } = useLocation()
|
|
11
|
-
|
|
12
|
-
// Helper to handle scroll logic
|
|
13
|
-
const handleScroll = (behavior: ScrollBehavior = 'auto') => {
|
|
14
|
-
const container = document.querySelector('.boltdocs-content') || window
|
|
15
|
-
|
|
16
|
-
const getScrollTop = () => {
|
|
17
|
-
if (container === window) return window.scrollY
|
|
18
|
-
return (container as HTMLElement).scrollTop
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const scrollTo = (top: number, scrollBehavior: ScrollBehavior) => {
|
|
22
|
-
if (container === window) {
|
|
23
|
-
window.scrollTo({ top, behavior: scrollBehavior })
|
|
24
|
-
} else {
|
|
25
|
-
;(container as HTMLElement).scrollTo({ top, behavior: scrollBehavior })
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (hash) {
|
|
30
|
-
const id = hash.replace('#', '')
|
|
31
|
-
const element = document.getElementById(id)
|
|
32
|
-
if (element) {
|
|
33
|
-
const offset = 80
|
|
34
|
-
const containerTop =
|
|
35
|
-
container === window
|
|
36
|
-
? 0
|
|
37
|
-
: (container as HTMLElement).getBoundingClientRect().top
|
|
38
|
-
const elementRect = element.getBoundingClientRect().top
|
|
39
|
-
const elementPosition = elementRect - containerTop
|
|
40
|
-
const offsetPosition = elementPosition - offset + getScrollTop()
|
|
41
|
-
|
|
42
|
-
scrollTo(offsetPosition, behavior)
|
|
43
|
-
return true
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
scrollTo(0, behavior)
|
|
48
|
-
return false
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// 1. Immediate sync scroll before paint
|
|
52
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies: pathname is used as a trigger for scroll-to-top on navigation
|
|
53
|
-
useLayoutEffect(() => {
|
|
54
|
-
handleScroll('auto')
|
|
55
|
-
}, [pathname, hash])
|
|
56
|
-
|
|
57
|
-
// 2. Delayed async scroll as fallback/stabilizer after paint & passive effects
|
|
58
|
-
useEffect(() => {
|
|
59
|
-
// Immediate run after paint (helps override old component unmount/revert side effects)
|
|
60
|
-
handleScroll('auto')
|
|
61
|
-
|
|
62
|
-
// Double-check inside requestAnimationFrame to catch concurrent renders or dynamic layout recalculations
|
|
63
|
-
const rafId = requestAnimationFrame(() => {
|
|
64
|
-
handleScroll('auto')
|
|
65
|
-
// Dispatch resize event so external components/scroll libraries (like GSAP ScrollTrigger) recalculate trigger offsets
|
|
66
|
-
window.dispatchEvent(new Event('resize'))
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
return () => cancelAnimationFrame(rafId)
|
|
70
|
-
}, [pathname, hash])
|
|
71
|
-
|
|
72
|
-
return null
|
|
73
|
-
}
|
|
74
|
-
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { createContext, use, useState, useEffect } from 'react'
|
|
2
|
-
|
|
3
|
-
export type Theme = 'light' | 'dark' | 'system'
|
|
4
|
-
export type ResolvedTheme = 'light' | 'dark'
|
|
5
|
-
|
|
6
|
-
interface ThemeContextType {
|
|
7
|
-
theme: Theme
|
|
8
|
-
resolvedTheme: ResolvedTheme
|
|
9
|
-
setTheme: (theme: Theme) => void
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const THEME_CONTEXT_SYMBOL = Symbol.for('__BDOCS_THEME_CONTEXT__')
|
|
13
|
-
const THEME_INSTANCE_SYMBOL = Symbol.for('__BDOCS_THEME_INSTANCE__')
|
|
14
|
-
const THEME_EVENT = 'boltdocs-theme-change'
|
|
15
|
-
|
|
16
|
-
const ThemeContext =
|
|
17
|
-
(globalThis as any)[THEME_CONTEXT_SYMBOL] ||
|
|
18
|
-
((globalThis as any)[THEME_CONTEXT_SYMBOL] = createContext<
|
|
19
|
-
ThemeContextType | undefined
|
|
20
|
-
>(undefined))
|
|
21
|
-
|
|
22
|
-
export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
|
23
|
-
const [theme, setThemeState] = useState<Theme>('system')
|
|
24
|
-
const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>('dark')
|
|
25
|
-
|
|
26
|
-
const applyTheme = (targetTheme: Theme) => {
|
|
27
|
-
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
|
28
|
-
const isDark =
|
|
29
|
-
targetTheme === 'dark' || (targetTheme === 'system' && mediaQuery.matches)
|
|
30
|
-
|
|
31
|
-
const root = window.document.documentElement
|
|
32
|
-
root.classList.toggle('dark', isDark)
|
|
33
|
-
root.dataset.theme = isDark ? 'dark' : 'light'
|
|
34
|
-
setResolvedTheme(isDark ? 'dark' : 'light')
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
useEffect(() => {
|
|
38
|
-
const savedTheme = localStorage.getItem('boltdocs-theme') as Theme | null
|
|
39
|
-
if (savedTheme) {
|
|
40
|
-
setThemeState(savedTheme)
|
|
41
|
-
applyTheme(savedTheme)
|
|
42
|
-
} else {
|
|
43
|
-
applyTheme('system')
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
|
47
|
-
const listener = () => {
|
|
48
|
-
const current =
|
|
49
|
-
(localStorage.getItem('boltdocs-theme') as Theme) || 'system'
|
|
50
|
-
if (current === 'system') applyTheme('system')
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
mediaQuery.addEventListener('change', listener)
|
|
54
|
-
return () => mediaQuery.removeEventListener('change', listener)
|
|
55
|
-
}, [])
|
|
56
|
-
|
|
57
|
-
const setTheme = (newTheme: Theme) => {
|
|
58
|
-
setThemeState(newTheme)
|
|
59
|
-
localStorage.setItem('boltdocs-theme', newTheme)
|
|
60
|
-
applyTheme(newTheme)
|
|
61
|
-
|
|
62
|
-
// Notify external listeners (dual-package hazard)
|
|
63
|
-
if (typeof window !== 'undefined') {
|
|
64
|
-
window.dispatchEvent(new CustomEvent(THEME_EVENT, { detail: newTheme }))
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const value = { theme, resolvedTheme, setTheme }
|
|
69
|
-
|
|
70
|
-
// Sync with global registry
|
|
71
|
-
if (typeof globalThis !== 'undefined') {
|
|
72
|
-
;(globalThis as any)[THEME_INSTANCE_SYMBOL] = value
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export function useTheme() {
|
|
79
|
-
const context = use(ThemeContext)
|
|
80
|
-
const [, forceUpdate] = useState({})
|
|
81
|
-
|
|
82
|
-
useEffect(() => {
|
|
83
|
-
if (context) return
|
|
84
|
-
|
|
85
|
-
const handler = () => forceUpdate({})
|
|
86
|
-
window.addEventListener(THEME_EVENT, handler)
|
|
87
|
-
return () => window.removeEventListener(THEME_EVENT, handler)
|
|
88
|
-
}, [context])
|
|
89
|
-
|
|
90
|
-
// Fallback to global registry for dual-package hazards
|
|
91
|
-
if (
|
|
92
|
-
!context &&
|
|
93
|
-
typeof globalThis !== 'undefined' &&
|
|
94
|
-
(globalThis as any)[THEME_INSTANCE_SYMBOL]
|
|
95
|
-
) {
|
|
96
|
-
return (globalThis as any)[THEME_INSTANCE_SYMBOL] as ThemeContextType
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (context === undefined) {
|
|
100
|
-
throw new Error('useTheme must be used within a ThemeProvider')
|
|
101
|
-
}
|
|
102
|
-
return context as ThemeContextType
|
|
103
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { createContext, useContext, useState, useEffect } from 'react'
|
|
2
|
-
import { useLocation } from 'react-router-dom'
|
|
3
|
-
|
|
4
|
-
interface UIContextType {
|
|
5
|
-
isSidebarOpen: boolean
|
|
6
|
-
toggleSidebar: () => void
|
|
7
|
-
closeSidebar: () => void
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const UIContext = createContext<UIContextType | undefined>(undefined)
|
|
11
|
-
|
|
12
|
-
export function UIProvider({ children }: { children: React.ReactNode }) {
|
|
13
|
-
const [isSidebarOpen, setIsSidebarOpen] = useState(false)
|
|
14
|
-
const location = useLocation()
|
|
15
|
-
|
|
16
|
-
const toggleSidebar = () => setIsSidebarOpen((prev) => !prev)
|
|
17
|
-
const closeSidebar = () => setIsSidebarOpen(false)
|
|
18
|
-
|
|
19
|
-
// Close sidebar on navigation
|
|
20
|
-
useEffect(() => {
|
|
21
|
-
setIsSidebarOpen(false)
|
|
22
|
-
}, [location.pathname])
|
|
23
|
-
|
|
24
|
-
return (
|
|
25
|
-
<UIContext.Provider value={{ isSidebarOpen, toggleSidebar, closeSidebar }}>
|
|
26
|
-
{children}
|
|
27
|
-
</UIContext.Provider>
|
|
28
|
-
)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function useUI() {
|
|
32
|
-
const context = useContext(UIContext)
|
|
33
|
-
if (context === undefined) {
|
|
34
|
-
// Safe fallback for split bundles, independent component renders, or during SSR
|
|
35
|
-
return {
|
|
36
|
-
isSidebarOpen: false,
|
|
37
|
-
toggleSidebar: () => {},
|
|
38
|
-
closeSidebar: () => {},
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return context
|
|
42
|
-
}
|