mirror of
https://github.com/tabler/tabler.git
synced 2026-10-12 05:06:55 +04:00
Add llms-full.txt and AI-readability signals to the docs (#2911)
This commit is contained in:
1 parent
dbf2663efe
commit
73f7c2a80b
16 files changed
+341
-97
No files matched your search
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@tabler/docs": minor
|
||||
---
|
||||
|
||||
Added `Accept: text/markdown` content negotiation for docs pages, with q-value parsing and `406` responses.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@tabler/docs": minor
|
||||
---
|
||||
|
||||
Added a `llms-full.txt` page with all docs in one file, `Content-Signal` in robots.txt and markdown `Link` alternate headers.
|
||||
+3
-57
@@ -6,6 +6,7 @@ import { satteri } from '@astrojs/markdown-satteri'
|
||||
import { unwrapJsxParagraphs } from './lib/satteri-unwrap-jsx-paragraphs.mjs'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { copyAssets } from '../.build/copy-assets'
|
||||
import { redirects } from './lib/redirects.ts'
|
||||
|
||||
/** @param {string} p */
|
||||
const path = (p) => fileURLToPath(new URL(p, import.meta.url))
|
||||
@@ -30,63 +31,8 @@ export default defineConfig({
|
||||
enabled: true,
|
||||
},
|
||||
}),
|
||||
// Renamed/moved pages. Add an entry here whenever a docs URL changes.
|
||||
// The plural component slugs are the pre-Astro URLs still present in the
|
||||
// Google index, Algolia search results and external backlinks.
|
||||
redirects: {
|
||||
'/ui/base/markdown': { status: 301, destination: '/ui/base/prose' },
|
||||
// @tabler/icons-eps is no longer maintained; PDF is the vector format to use.
|
||||
'/icons/static-files/eps': { status: 301, destination: '/icons/static-files/pdf' },
|
||||
...Object.fromEntries(
|
||||
[
|
||||
['alerts', 'alert'],
|
||||
['avatars', 'avatar'],
|
||||
['badges', 'badge'],
|
||||
['buttons', 'button'],
|
||||
['cards', 'card'],
|
||||
['dropdowns', 'dropdown'],
|
||||
['icons', 'icon'],
|
||||
['modals', 'modal'],
|
||||
['ribbons', 'ribbon'],
|
||||
['spinners', 'spinner'],
|
||||
['statuses', 'status'],
|
||||
['steps', 'step'],
|
||||
['tables', 'table'],
|
||||
['tabs', 'tab'],
|
||||
['timelines', 'timeline'],
|
||||
['toasts', 'toast'],
|
||||
['tooltips', 'tooltip'],
|
||||
].map(([from, to]) => [`/ui/components/${from}`, { status: 301, destination: `/ui/components/${to}` }]),
|
||||
),
|
||||
// Components that need a third-party library moved to /ui/plugins/.
|
||||
...Object.fromEntries(['autosize', 'chart', 'countup', 'dropzone', 'fullcalendar', 'inline-player', 'lightbox', 'range-slider', 'signature', 'vector-map', 'wysiwyg'].map((slug) => [`/ui/components/${slug}`, { status: 301, destination: `/ui/plugins/${slug}` }])),
|
||||
// Pre-Astro plural urls for two of those pages, sent straight to the new home.
|
||||
'/ui/components/charts': { status: 301, destination: '/ui/plugins/chart' },
|
||||
'/ui/components/vector-maps': { status: 301, destination: '/ui/plugins/vector-map' },
|
||||
// The form- prefix was redundant inside /ui/forms/.
|
||||
'/ui/forms/form-elements': { status: 301, destination: '/ui/forms/elements' },
|
||||
'/ui/forms/form-fieldset': { status: 301, destination: '/ui/forms/fieldset' },
|
||||
'/ui/forms/form-floating': { status: 301, destination: '/ui/forms/floating-labels' },
|
||||
'/ui/forms/form-helpers': { status: 301, destination: '/ui/forms/helpers' },
|
||||
'/ui/forms/form-selectboxes': { status: 301, destination: '/ui/forms/select-group' },
|
||||
'/ui/forms/form-image-check': { status: 301, destination: '/ui/forms/image-check' },
|
||||
'/ui/forms/form-color-check': { status: 301, destination: '/ui/forms/color-check' },
|
||||
'/ui/forms/form-select-tomselect': { status: 301, destination: '/ui/plugins/advanced-select' },
|
||||
'/ui/forms/form-colorpicker': { status: 301, destination: '/ui/plugins/color-picker' },
|
||||
'/ui/forms/form-datepicker': { status: 301, destination: '/ui/plugins/date-picker' },
|
||||
'/ui/forms/form-input-mask': { status: 301, destination: '/ui/plugins/input-mask' },
|
||||
'/ui/forms/form-validation': { status: 301, destination: '/ui/forms/validation' },
|
||||
// Illustrations and Emails lost their "introduction" wrapper.
|
||||
'/illustrations/introduction': { status: 301, destination: '/illustrations' },
|
||||
'/illustrations/introduction/preview': { status: 301, destination: '/illustrations/preview' },
|
||||
'/illustrations/introduction/contents': { status: 301, destination: '/illustrations/contents' },
|
||||
'/illustrations/introduction/customization': { status: 301, destination: '/illustrations/customization' },
|
||||
'/illustrations/introduction/license': { status: 301, destination: '/illustrations/license' },
|
||||
'/emails/introduction': { status: 301, destination: '/emails' },
|
||||
'/emails/introduction/contents': { status: 301, destination: '/emails/contents' },
|
||||
'/emails/introduction/compiled-html': { status: 301, destination: '/emails/compiled-html' },
|
||||
'/emails/introduction/source-html': { status: 301, destination: '/emails/source-html' },
|
||||
},
|
||||
// renamed/moved pages, shared with middleware.ts
|
||||
redirects,
|
||||
// pages live at the package root (./pages) — content-first layout; all
|
||||
// components/lib/data are shared (see the @shared alias). The docs content
|
||||
// itself lives in ./content and is rendered by pages/[...slug].astro.
|
||||
|
||||
@@ -302,6 +302,13 @@ const relatedPages = await Promise.all(
|
||||
|
||||
<body class="d-flex flex-column">
|
||||
<a href="#content" class="visually-hidden-focusable skip-link">Skip to main content</a>
|
||||
{
|
||||
markdownUrl && (
|
||||
<div class="visually-hidden" aria-hidden="true">
|
||||
A Markdown version of this page is available at {`${site.docsUrl}${markdownUrl}`}.
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<!-- BEGIN GLOBAL THEME SCRIPT -->
|
||||
<script is:inline src="/dist/js/tabler-theme.js"></script>
|
||||
<script is:inline>
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
// Accept negotiation between text/html and text/markdown (RFC 9110 q-values).
|
||||
// A tie goes to markdown only when the client named text/markdown explicitly,
|
||||
// so `Accept: */*` from a browser still gets html; 'none' means answer 406.
|
||||
|
||||
type MediaRange = {
|
||||
type: string
|
||||
subtype: string
|
||||
q: number
|
||||
}
|
||||
|
||||
export type Format = 'html' | 'markdown' | 'none'
|
||||
|
||||
function parseAccept(header: string): MediaRange[] {
|
||||
return header
|
||||
.split(',')
|
||||
.map((part): MediaRange => {
|
||||
const [range = '', ...params] = part.trim().split(';')
|
||||
const [type = '', subtype = ''] = range.trim().toLowerCase().split('/')
|
||||
let q = 1
|
||||
for (const param of params) {
|
||||
const [key, value] = param.split('=')
|
||||
if (key?.trim().toLowerCase() === 'q') {
|
||||
const parsed = Number.parseFloat(value ?? '')
|
||||
// a present but unparsable q fails closed
|
||||
q = Number.isNaN(parsed) ? 0 : Math.min(Math.max(parsed, 0), 1)
|
||||
}
|
||||
}
|
||||
return { type, subtype, q }
|
||||
})
|
||||
.filter((range) => range.type !== '' && range.subtype !== '')
|
||||
}
|
||||
|
||||
// The q of the most specific matching range: exact type beats `text/*` beats `*/*`.
|
||||
function match(ranges: MediaRange[], type: string, subtype: string) {
|
||||
let q = 0
|
||||
let specificity = -1
|
||||
let explicit = false
|
||||
for (const range of ranges) {
|
||||
let rangeSpecificity: number
|
||||
if (range.type === type && range.subtype === subtype) rangeSpecificity = 2
|
||||
else if (range.type === type && range.subtype === '*') rangeSpecificity = 1
|
||||
else if (range.type === '*' && range.subtype === '*') rangeSpecificity = 0
|
||||
else continue
|
||||
if (rangeSpecificity > specificity) {
|
||||
specificity = rangeSpecificity
|
||||
q = range.q
|
||||
explicit = rangeSpecificity === 2
|
||||
}
|
||||
}
|
||||
return { q, explicit }
|
||||
}
|
||||
|
||||
export function preferredFormat(header: string | null): Format {
|
||||
if (!header || header.trim() === '') return 'html'
|
||||
const ranges = parseAccept(header)
|
||||
if (ranges.length === 0) return 'html'
|
||||
|
||||
const html = match(ranges, 'text', 'html')
|
||||
const markdown = match(ranges, 'text', 'markdown')
|
||||
|
||||
if (markdown.q > html.q) return 'markdown'
|
||||
if (markdown.q === html.q && markdown.q > 0 && markdown.explicit) return 'markdown'
|
||||
if (html.q > 0) return 'html'
|
||||
if (markdown.q > 0) return 'markdown'
|
||||
return 'none'
|
||||
}
|
||||
+30
-4
@@ -17,6 +17,7 @@ import { experimental_AstroContainer as AstroContainer } from 'astro/container'
|
||||
import { getContainerRenderer } from '@astrojs/mdx/container-renderer'
|
||||
import { beautifyHtml, extractMarkedSnippet } from '@shared/lib/code-example'
|
||||
import { site } from '@shared/lib/site'
|
||||
import docs from '@data/docs.json'
|
||||
import packageManagers from '@data/package-managers.json'
|
||||
import { cdnCssTag, cdnJsTag, cdnPackageSnippet, cdnPluginSnippet } from './cdn-snippets.ts'
|
||||
|
||||
@@ -154,8 +155,8 @@ async function renderedExamples(entry: CollectionEntry<'docs'>): Promise<(string
|
||||
const markup = block.match(/data-clipboard-text="([^"]*)"/) ?? block.match(/data-example-markup="([^"]*)"/)
|
||||
return markup ? beautifyHtml(decodeEntities(markup[1]!)) : null
|
||||
})
|
||||
} catch {
|
||||
// A page that fails to render still gets its prose and its source-level examples.
|
||||
} catch (error) {
|
||||
console.warn(`[llms] Rendering ${entry.id} failed, examples fall back to their MDX source:`, error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
@@ -222,13 +223,38 @@ export async function mdxToMarkdown(body: string, examples: (string | null)[] =
|
||||
.trim()
|
||||
}
|
||||
|
||||
/** A single docs page as a standalone markdown document. */
|
||||
export async function pageMarkdown(entry: CollectionEntry<'docs'>, url: string): Promise<string> {
|
||||
async function buildPageMarkdown(entry: CollectionEntry<'docs'>, url: string): Promise<string> {
|
||||
const { title, summary, description } = entry.data
|
||||
const header = [`# ${title}`, '', `> ${summary}`, '', description, '', `Source: ${url}`, '', '---', ''].join('\n')
|
||||
|
||||
return `${header}\n${await mdxToMarkdown(entry.body ?? '', await renderedExamples(entry))}\n`
|
||||
}
|
||||
|
||||
// [...slug].md.ts and llms-full.txt.ts render the same pages in one build.
|
||||
const pageCache = new Map<string, Promise<string>>()
|
||||
|
||||
/** A single docs page as a standalone markdown document. */
|
||||
export function pageMarkdown(entry: CollectionEntry<'docs'>, url: string): Promise<string> {
|
||||
const key = `${entry.id}\n${url}`
|
||||
let cached = pageCache.get(key)
|
||||
if (!cached) {
|
||||
cached = buildPageMarkdown(entry, url)
|
||||
pageCache.set(key, cached)
|
||||
}
|
||||
return cached
|
||||
}
|
||||
|
||||
type MenuNode = { url?: string; children?: MenuNode[] }
|
||||
|
||||
/** Docs urls in sidebar (docs.json) order — the shared reading order of llms.txt and llms-full.txt. */
|
||||
export function menuOrderedUrls(): string[] {
|
||||
const normalize = (url: string) => {
|
||||
const parts = url.split('/').filter(Boolean)
|
||||
return parts.length ? `/${parts.join('/')}` : '/'
|
||||
}
|
||||
const walk = (nodes: MenuNode[]): string[] => nodes.flatMap((node) => [...(node.url ? [normalize(node.url)] : []), ...walk(node.children ?? [])])
|
||||
return [...new Set(walk(docs.menu as MenuNode[]))]
|
||||
}
|
||||
|
||||
/** Absolute in production, root-relative in dev — same rule as sitemap.xml.ts. */
|
||||
export const baseUrl = () => (process.env.NODE_ENV === 'development' ? '' : site.docsUrl)
|
||||
@@ -0,0 +1,4 @@
|
||||
// The `.md` mirror of a docs page url; the mirror of `/` is `/index.md`.
|
||||
// The same rule is hand-encoded in vercel.json's Link-header routes.
|
||||
// Dependency-free so Vercel's middleware bundler can include it.
|
||||
export const markdownUrlFromDocsUrl = (url: string) => (url === '/' ? '/index.md' : `${url.replace(/\/$/, '')}.md`)
|
||||
@@ -0,0 +1,61 @@
|
||||
// Renamed/moved docs pages; add an entry whenever a docs URL changes.
|
||||
// Shared by astro.config.mjs (real HTTP redirects) and middleware.ts (which
|
||||
// must let these urls fall through instead of rewriting them to `.md`).
|
||||
|
||||
type Redirect = { status: 301; destination: string }
|
||||
|
||||
export const redirects: Record<string, Redirect> = {
|
||||
'/ui/base/markdown': { status: 301, destination: '/ui/base/prose' },
|
||||
// @tabler/icons-eps is no longer maintained; PDF is the vector format to use.
|
||||
'/icons/static-files/eps': { status: 301, destination: '/icons/static-files/pdf' },
|
||||
// pre-Astro plural slugs, still present in the Google index and backlinks
|
||||
...Object.fromEntries(
|
||||
[
|
||||
['alerts', 'alert'],
|
||||
['avatars', 'avatar'],
|
||||
['badges', 'badge'],
|
||||
['buttons', 'button'],
|
||||
['cards', 'card'],
|
||||
['dropdowns', 'dropdown'],
|
||||
['icons', 'icon'],
|
||||
['modals', 'modal'],
|
||||
['ribbons', 'ribbon'],
|
||||
['spinners', 'spinner'],
|
||||
['statuses', 'status'],
|
||||
['steps', 'step'],
|
||||
['tables', 'table'],
|
||||
['tabs', 'tab'],
|
||||
['timelines', 'timeline'],
|
||||
['toasts', 'toast'],
|
||||
['tooltips', 'tooltip'],
|
||||
].map(([from, to]): [string, Redirect] => [`/ui/components/${from}`, { status: 301, destination: `/ui/components/${to}` }]),
|
||||
),
|
||||
// Components that need a third-party library moved to /ui/plugins/.
|
||||
...Object.fromEntries(['autosize', 'chart', 'countup', 'dropzone', 'fullcalendar', 'inline-player', 'lightbox', 'range-slider', 'signature', 'vector-map', 'wysiwyg'].map((slug): [string, Redirect] => [`/ui/components/${slug}`, { status: 301, destination: `/ui/plugins/${slug}` }])),
|
||||
// Pre-Astro plural urls for two of those pages, sent straight to the new home.
|
||||
'/ui/components/charts': { status: 301, destination: '/ui/plugins/chart' },
|
||||
'/ui/components/vector-maps': { status: 301, destination: '/ui/plugins/vector-map' },
|
||||
// The form- prefix was redundant inside /ui/forms/.
|
||||
'/ui/forms/form-elements': { status: 301, destination: '/ui/forms/elements' },
|
||||
'/ui/forms/form-fieldset': { status: 301, destination: '/ui/forms/fieldset' },
|
||||
'/ui/forms/form-floating': { status: 301, destination: '/ui/forms/floating-labels' },
|
||||
'/ui/forms/form-helpers': { status: 301, destination: '/ui/forms/helpers' },
|
||||
'/ui/forms/form-selectboxes': { status: 301, destination: '/ui/forms/select-group' },
|
||||
'/ui/forms/form-image-check': { status: 301, destination: '/ui/forms/image-check' },
|
||||
'/ui/forms/form-color-check': { status: 301, destination: '/ui/forms/color-check' },
|
||||
'/ui/forms/form-select-tomselect': { status: 301, destination: '/ui/plugins/advanced-select' },
|
||||
'/ui/forms/form-colorpicker': { status: 301, destination: '/ui/plugins/color-picker' },
|
||||
'/ui/forms/form-datepicker': { status: 301, destination: '/ui/plugins/date-picker' },
|
||||
'/ui/forms/form-input-mask': { status: 301, destination: '/ui/plugins/input-mask' },
|
||||
'/ui/forms/form-validation': { status: 301, destination: '/ui/forms/validation' },
|
||||
// Illustrations and Emails lost their "introduction" wrapper.
|
||||
'/illustrations/introduction': { status: 301, destination: '/illustrations' },
|
||||
'/illustrations/introduction/preview': { status: 301, destination: '/illustrations/preview' },
|
||||
'/illustrations/introduction/contents': { status: 301, destination: '/illustrations/contents' },
|
||||
'/illustrations/introduction/customization': { status: 301, destination: '/illustrations/customization' },
|
||||
'/illustrations/introduction/license': { status: 301, destination: '/illustrations/license' },
|
||||
'/emails/introduction': { status: 301, destination: '/emails' },
|
||||
'/emails/introduction/contents': { status: 301, destination: '/emails/contents' },
|
||||
'/emails/introduction/compiled-html': { status: 301, destination: '/emails/compiled-html' },
|
||||
'/emails/introduction/source-html': { status: 301, destination: '/emails/source-html' },
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// Vercel Routing Middleware (https://vercel.com/docs/routing-middleware):
|
||||
// serves the markdown mirror of a docs page when the client prefers it via
|
||||
// `Accept: text/markdown`. Runs only on Vercel deployments.
|
||||
import type { MiddlewareHandler } from 'astro'
|
||||
import { next, rewrite } from '@vercel/functions'
|
||||
import { preferredFormat } from './lib/accept'
|
||||
import { markdownUrlFromDocsUrl } from './lib/markdown-url'
|
||||
import { redirects } from './lib/redirects'
|
||||
|
||||
// Astro picks this file up as its own middleware (`srcDir` is the project
|
||||
// root) and requires an `onRequest` export; the real logic is Vercel-only.
|
||||
export const onRequest: MiddlewareHandler = (_context, nextHandler) => nextHandler()
|
||||
|
||||
export const config = {
|
||||
// extensionless paths outside the asset directories — the html docs pages
|
||||
matcher: '/((?!(?:dist|preview|img|static|css|js)/)(?!.*\\.).*)',
|
||||
}
|
||||
|
||||
const alternate = (path: string, type: string) => `<${path}>; rel="alternate"; type="${type}"`
|
||||
|
||||
export default function middleware(request: Request) {
|
||||
const url = new URL(request.url)
|
||||
|
||||
// renamed pages fall through to their 301; negotiation reruns on the target
|
||||
if (Object.hasOwn(redirects, url.pathname)) return next()
|
||||
|
||||
const format = preferredFormat(request.headers.get('accept'))
|
||||
|
||||
if (format === 'markdown') {
|
||||
const htmlPath = url.pathname
|
||||
url.pathname = markdownUrlFromDocsUrl(url.pathname)
|
||||
return rewrite(url, {
|
||||
headers: {
|
||||
Link: alternate(htmlPath, 'text/html'),
|
||||
Vary: 'Accept',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if (format === 'none') {
|
||||
return new Response('Not acceptable. This page is available as text/html or text/markdown.\n', {
|
||||
status: 406,
|
||||
headers: {
|
||||
'Content-Type': 'text/plain; charset=utf-8',
|
||||
'Vary': 'Accept',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return next({
|
||||
headers: {
|
||||
Link: alternate(markdownUrlFromDocsUrl(url.pathname), 'text/markdown'),
|
||||
Vary: 'Accept',
|
||||
},
|
||||
})
|
||||
}
|
||||
+2
-1
@@ -19,14 +19,15 @@
|
||||
"@astrojs/vercel": "^11.0.5",
|
||||
"@docsearch/js": "^4.7.0",
|
||||
"@tabler/core": "workspace:*",
|
||||
"@vercel/functions": "^3.9.5",
|
||||
"astro": "^7.1.6",
|
||||
"shiki": "^4.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/check": "^0.9.4",
|
||||
"@docsearch/css": "^4.7.0",
|
||||
"@astrojs/markdown-satteri": "^0.3.4",
|
||||
"@astrojs/mdx": "^7.0.3",
|
||||
"@docsearch/css": "^4.7.0",
|
||||
"@tabler/preview": "workspace:*",
|
||||
"@types/node": "^26.1.1",
|
||||
"js-beautify": "^2.0.3",
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { GetStaticPaths } from 'astro'
|
||||
import { getCollection, render } from 'astro:content'
|
||||
import DocsLayout from '@layouts/DocsLayout.astro'
|
||||
import { docsUrlFromId } from '@lib/docs-pages'
|
||||
import { markdownUrlFromDocsUrl } from '@lib/markdown-url'
|
||||
|
||||
export const getStaticPaths = (async () => {
|
||||
const entries = await getCollection('docs')
|
||||
@@ -45,7 +46,7 @@ if (data.classnames) {
|
||||
sourcePath={data.source}
|
||||
toc={toc}
|
||||
url={docsUrlFromId(entry.id)}
|
||||
markdownUrl={`/${entry.id}.md`}
|
||||
markdownUrl={markdownUrlFromDocsUrl(docsUrlFromId(entry.id))}
|
||||
editPath={entry.filePath ? `docs/${entry.filePath}` : undefined}
|
||||
>
|
||||
<Content />
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// https://llmstxt.org — the whole docs as one markdown file. Pages are the
|
||||
// same markdown the `.md` mirrors serve, in sidebar order (home page first,
|
||||
// pages missing from the sidebar tree last).
|
||||
import type { APIRoute } from 'astro'
|
||||
import { getCollection } from 'astro:content'
|
||||
import { site } from '@shared/lib/site'
|
||||
import { docsUrlFromId } from '@lib/docs-pages'
|
||||
import { baseUrl, menuOrderedUrls, pageMarkdown } from '@lib/llms'
|
||||
|
||||
export const prerender = true
|
||||
|
||||
export const GET: APIRoute = async () => {
|
||||
const menuRank = new Map(menuOrderedUrls().map((url, index) => [url, index]))
|
||||
const rank = (url: string) => menuRank.get(url) ?? Infinity
|
||||
const entries = (await getCollection('docs')).sort((a, b) => {
|
||||
const [urlA, urlB] = [docsUrlFromId(a.id), docsUrlFromId(b.id)]
|
||||
if (urlA === '/' || urlB === '/') return urlA === '/' ? -1 : 1
|
||||
return rank(urlA) - rank(urlB) || urlA.localeCompare(urlB)
|
||||
})
|
||||
|
||||
const base = baseUrl()
|
||||
const pages = await Promise.all(entries.map((entry) => pageMarkdown(entry, `${base}${docsUrlFromId(entry.id)}`)))
|
||||
|
||||
const body = `# Tabler — full documentation
|
||||
|
||||
> ${site.description}
|
||||
|
||||
This is the complete documentation of ${site.docsUrl} as a single markdown file. A curated index of the same pages lives at ${site.docsUrl}/llms.txt.
|
||||
|
||||
---
|
||||
|
||||
${pages.join('\n\n---\n\n')}
|
||||
`
|
||||
|
||||
return new Response(body, {
|
||||
headers: {
|
||||
'Content-Type': 'text/plain; charset=utf-8',
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import docs from '@data/docs.json'
|
||||
import { site } from '@shared/lib/site'
|
||||
import { docsUrlFromId } from '@lib/docs-pages'
|
||||
import { baseUrl } from '@lib/llms'
|
||||
import { markdownUrlFromDocsUrl } from '@lib/markdown-url'
|
||||
|
||||
export const prerender = true
|
||||
|
||||
@@ -28,8 +29,7 @@ export const GET: APIRoute = async () => {
|
||||
const entry = byUrl.get(url)
|
||||
if (!entry || listed.has(url)) return null
|
||||
listed.add(url)
|
||||
// `/` has no `.md` sibling — its mirror is `/index.md`
|
||||
return `- [${entry.data.title}](${base}${url === '/' ? '/index' : url}.md): ${entry.data.description}`
|
||||
return `- [${entry.data.title}](${base}${markdownUrlFromDocsUrl(url)}): ${entry.data.description}`
|
||||
}
|
||||
|
||||
const pageUrls = (nodes: (MenuNode | undefined)[]) => nodes.flatMap((node) => [node?.url, ...(node?.children ?? []).map((child) => child.url)]).filter((url): url is string => typeof url === 'string' && url.startsWith('/'))
|
||||
@@ -85,7 +85,7 @@ export const GET: APIRoute = async () => {
|
||||
|
||||
Tabler is a free and open source dashboard UI kit built on Bootstrap. This file indexes the documentation at ${site.docsUrl}.
|
||||
|
||||
Every page is available as markdown by appending \`.md\` to its url, for example \`${site.docsUrl}/ui/components/button.md\`. Those files contain the same prose as the html page plus the markup of every example, as fenced code blocks.
|
||||
Every page is available as markdown by appending \`.md\` to its url, for example \`${site.docsUrl}/ui/components/button.md\`. Those files contain the same prose as the html page plus the markup of every example, as fenced code blocks. The whole documentation concatenated into one file is available at \`${site.docsUrl}/llms-full.txt\`.
|
||||
|
||||
The markup is taken from the rendered pages, so every example is plain html — the same markup the copy button on the page gives you.
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ export const GET: APIRoute = () => {
|
||||
const body = `Sitemap: ${sitemapUrl}
|
||||
|
||||
User-agent: *
|
||||
Content-Signal: search=yes, ai-input=yes, ai-train=yes
|
||||
Disallow:${isProduction ? '' : ' /'}
|
||||
`
|
||||
|
||||
|
||||
@@ -8,6 +8,28 @@
|
||||
{
|
||||
"source": "/(dist|preview|img|static|css|js)/(.*)",
|
||||
"headers": [{ "key": "Cache-Control", "value": "public, max-age=3600, stale-while-revalidate=86400" }]
|
||||
},
|
||||
{
|
||||
"source": "/(llms|llms-full)\\.txt",
|
||||
"headers": [{ "key": "Cache-Control", "value": "public, max-age=3600, stale-while-revalidate=86400" }]
|
||||
},
|
||||
{
|
||||
"source": "/:path([^.]+)\\.md",
|
||||
"headers": [{ "key": "Cache-Control", "value": "public, max-age=3600, stale-while-revalidate=86400" }]
|
||||
},
|
||||
{
|
||||
"source": "/:path((?!index$)[^.]+)\\.md",
|
||||
"headers": [
|
||||
{ "key": "Link", "value": "</:path>; rel=\"alternate\"; type=\"text/html\"" },
|
||||
{ "key": "Vary", "value": "Accept" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"source": "/index.md",
|
||||
"headers": [
|
||||
{ "key": "Link", "value": "</>; rel=\"alternate\"; type=\"text/html\"" },
|
||||
{ "key": "Vary", "value": "Accept" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Generated
+34
-31
@@ -213,16 +213,19 @@ importers:
|
||||
dependencies:
|
||||
'@astrojs/vercel':
|
||||
specifier: ^11.0.5
|
||||
version: 11.0.5(astro@7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.1(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0))(next@16.2.10(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.102.0))(react@19.2.7)(supports-color@10.2.2)(ws@8.21.0)
|
||||
version: 11.0.5(astro@7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.5(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0))(next@16.2.10(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.102.0))(react@19.2.7)(supports-color@10.2.2)(ws@8.21.0)
|
||||
'@docsearch/js':
|
||||
specifier: ^4.7.0
|
||||
version: 4.7.0
|
||||
'@tabler/core':
|
||||
specifier: workspace:*
|
||||
version: link:../core
|
||||
'@vercel/functions':
|
||||
specifier: ^3.9.5
|
||||
version: 3.9.5(ws@8.21.0)
|
||||
astro:
|
||||
specifier: ^7.1.6
|
||||
version: 7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.1(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0)
|
||||
version: 7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.5(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0)
|
||||
shiki:
|
||||
specifier: ^4.3.1
|
||||
version: 4.3.1
|
||||
@@ -235,7 +238,7 @@ importers:
|
||||
version: 0.3.4
|
||||
'@astrojs/mdx':
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3(@astrojs/markdown-satteri@0.3.4)(astro@7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.1(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0))(supports-color@10.2.2)
|
||||
version: 7.0.3(@astrojs/markdown-satteri@0.3.4)(astro@7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.5(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0))(supports-color@10.2.2)
|
||||
'@docsearch/css':
|
||||
specifier: ^4.7.0
|
||||
version: 4.7.0
|
||||
@@ -262,7 +265,7 @@ importers:
|
||||
version: link:../core
|
||||
astro:
|
||||
specifier: ^7.1.6
|
||||
version: 7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.1(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0)
|
||||
version: 7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.5(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0)
|
||||
devDependencies:
|
||||
'@astrojs/check':
|
||||
specifier: ^0.9.4
|
||||
@@ -272,7 +275,7 @@ importers:
|
||||
version: 0.3.4
|
||||
'@astrojs/mdx':
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3(@astrojs/markdown-satteri@0.3.4)(astro@7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.1(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0))(supports-color@5.5.0)
|
||||
version: 7.0.3(@astrojs/markdown-satteri@0.3.4)(astro@7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.5(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0))(supports-color@5.5.0)
|
||||
'@tabler/icons':
|
||||
specifier: ^3.45.0
|
||||
version: 3.45.0
|
||||
@@ -302,7 +305,7 @@ importers:
|
||||
version: link:../core
|
||||
astro:
|
||||
specifier: ^7.1.6
|
||||
version: 7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.1(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0)
|
||||
version: 7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.5(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0)
|
||||
devDependencies:
|
||||
'@astrojs/check':
|
||||
specifier: ^0.9.4
|
||||
@@ -340,7 +343,7 @@ importers:
|
||||
version: 1.14.3
|
||||
astro:
|
||||
specifier: ^7.1.3
|
||||
version: 7.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.1(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0)
|
||||
version: 7.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.5(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0)
|
||||
js-beautify:
|
||||
specifier: ^2.0.3
|
||||
version: 2.0.3
|
||||
@@ -2088,15 +2091,15 @@ packages:
|
||||
vue-router:
|
||||
optional: true
|
||||
|
||||
'@vercel/cli-config@0.2.2':
|
||||
resolution: {integrity: sha512-kAy35eymNzRBfmcqEViVQge0KJ59FYEKsPqGNCSJQ7M9HTuFGWd3qPcZos1A5cZpAWRBdiYe82Bcz9P7pIZvUQ==}
|
||||
'@vercel/cli-config@0.2.4':
|
||||
resolution: {integrity: sha512-kZ5SojbrV06GHoU6QIWGwDXLov+s9rWZ7QqdqKfJfBGCNUieGfgaCjeeenNy8Y+QC0bwC0dZ2B4l5Hvdmrgpdw==}
|
||||
|
||||
'@vercel/cli-exec@1.0.1':
|
||||
resolution: {integrity: sha512-g9XerViJ/paZujufXYcu5XYI2vU2rtB4sgdpjUHde5RnOkdmpu0ngH46LCFGHoPXO/C+qDPSczIHIRN+8Q2YKQ==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@vercel/functions@3.9.1':
|
||||
resolution: {integrity: sha512-RhrIZvQ0Jfgeq0pCaeZ/E19OujpGEyB2BDO//6F91Z8VC5JJ5H4ckLLswaKnM7vOutRswU/MO6UQMGhtm7fA/Q==}
|
||||
'@vercel/functions@3.9.5':
|
||||
resolution: {integrity: sha512-EUfqlb7AzoEh7URlMNAO4jbJiLWz9grDBHvfjKTDvEP9c8y3DqX3SWPvfaQkUjtkm3b83flhaUUMuewdHa+qmw==}
|
||||
engines: {node: '>= 20'}
|
||||
peerDependencies:
|
||||
'@aws-sdk/credential-provider-web-identity': '*'
|
||||
@@ -2112,8 +2115,8 @@ packages:
|
||||
engines: {node: '>=20'}
|
||||
hasBin: true
|
||||
|
||||
'@vercel/oidc@3.8.2':
|
||||
resolution: {integrity: sha512-nmVSeQ7tewCkqYBNB/MNL8aPB9sTvtjvqIE6+U17U4IuTyehuWVERDlWrSn0DVfiFAstRlRkGLHBlKHB2FyzbQ==}
|
||||
'@vercel/oidc@3.8.5':
|
||||
resolution: {integrity: sha512-RwXYtnt6za+5UO4IaLywN/6B95AlLqynPRUWRJxeJ/qufwkcLUbZNUxYtzT0uMpuraWhlNcGqPNGkTnZr4BGBw==}
|
||||
engines: {node: '>= 20'}
|
||||
|
||||
'@vercel/routing-utils@5.3.3':
|
||||
@@ -5504,13 +5507,13 @@ snapshots:
|
||||
hast-util-from-html: 2.0.3
|
||||
satteri: 0.9.5
|
||||
|
||||
'@astrojs/mdx@7.0.3(@astrojs/markdown-satteri@0.3.4)(astro@7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.1(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0))(supports-color@10.2.2)':
|
||||
'@astrojs/mdx@7.0.3(@astrojs/markdown-satteri@0.3.4)(astro@7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.5(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0))(supports-color@10.2.2)':
|
||||
dependencies:
|
||||
'@astrojs/internal-helpers': 0.10.1
|
||||
'@astrojs/markdown-remark': 7.2.1(supports-color@10.2.2)
|
||||
'@mdx-js/mdx': 3.1.1(supports-color@10.2.2)
|
||||
acorn: 8.17.0
|
||||
astro: 7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.1(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0)
|
||||
astro: 7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.5(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0)
|
||||
es-module-lexer: 2.3.0
|
||||
estree-util-visit: 2.0.0
|
||||
hast-util-to-html: 9.0.5
|
||||
@@ -5526,13 +5529,13 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@astrojs/mdx@7.0.3(@astrojs/markdown-satteri@0.3.4)(astro@7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.1(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0))(supports-color@5.5.0)':
|
||||
'@astrojs/mdx@7.0.3(@astrojs/markdown-satteri@0.3.4)(astro@7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.5(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0))(supports-color@5.5.0)':
|
||||
dependencies:
|
||||
'@astrojs/internal-helpers': 0.10.1
|
||||
'@astrojs/markdown-remark': 7.2.1(supports-color@5.5.0)
|
||||
'@mdx-js/mdx': 3.1.1(supports-color@5.5.0)
|
||||
acorn: 8.17.0
|
||||
astro: 7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.1(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0)
|
||||
astro: 7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.5(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0)
|
||||
es-module-lexer: 2.3.0
|
||||
estree-util-visit: 2.0.0
|
||||
hast-util-to-html: 9.0.5
|
||||
@@ -5559,14 +5562,14 @@ snapshots:
|
||||
is-docker: 4.0.0
|
||||
package-manager-detector: 1.7.0
|
||||
|
||||
'@astrojs/vercel@11.0.5(astro@7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.1(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0))(next@16.2.10(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.102.0))(react@19.2.7)(supports-color@10.2.2)(ws@8.21.0)':
|
||||
'@astrojs/vercel@11.0.5(astro@7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.5(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0))(next@16.2.10(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.102.0))(react@19.2.7)(supports-color@10.2.2)(ws@8.21.0)':
|
||||
dependencies:
|
||||
'@astrojs/internal-helpers': 0.10.2
|
||||
'@vercel/analytics': 1.6.1(next@16.2.10(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.102.0))(react@19.2.7)
|
||||
'@vercel/functions': 3.9.1(ws@8.21.0)
|
||||
'@vercel/functions': 3.9.5(ws@8.21.0)
|
||||
'@vercel/nft': 1.10.2(supports-color@10.2.2)
|
||||
'@vercel/routing-utils': 5.3.3
|
||||
astro: 7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.1(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0)
|
||||
astro: 7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.5(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0)
|
||||
esbuild: 0.28.1
|
||||
tinyglobby: 0.2.17
|
||||
transitivePeerDependencies:
|
||||
@@ -6870,7 +6873,7 @@ snapshots:
|
||||
next: 16.2.10(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.102.0)
|
||||
react: 19.2.7
|
||||
|
||||
'@vercel/cli-config@0.2.2':
|
||||
'@vercel/cli-config@0.2.4':
|
||||
dependencies:
|
||||
xdg-app-paths: 5.5.1
|
||||
zod: 4.1.11
|
||||
@@ -6879,9 +6882,9 @@ snapshots:
|
||||
dependencies:
|
||||
execa: 5.1.1
|
||||
|
||||
'@vercel/functions@3.9.1(ws@8.21.0)':
|
||||
'@vercel/functions@3.9.5(ws@8.21.0)':
|
||||
dependencies:
|
||||
'@vercel/oidc': 3.8.2
|
||||
'@vercel/oidc': 3.8.5
|
||||
optionalDependencies:
|
||||
ws: 8.21.0
|
||||
|
||||
@@ -6904,9 +6907,9 @@ snapshots:
|
||||
- rollup
|
||||
- supports-color
|
||||
|
||||
'@vercel/oidc@3.8.2':
|
||||
'@vercel/oidc@3.8.5':
|
||||
dependencies:
|
||||
'@vercel/cli-config': 0.2.2
|
||||
'@vercel/cli-config': 0.2.4
|
||||
'@vercel/cli-exec': 1.0.1
|
||||
jose: 5.10.0
|
||||
|
||||
@@ -7138,7 +7141,7 @@ snapshots:
|
||||
|
||||
astring@1.9.0: {}
|
||||
|
||||
astro@7.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.1(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0):
|
||||
astro@7.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.5(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0):
|
||||
dependencies:
|
||||
'@astrojs/compiler-rs': 0.3.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)
|
||||
'@astrojs/internal-helpers': 0.10.2
|
||||
@@ -7187,7 +7190,7 @@ snapshots:
|
||||
tinyglobby: 0.2.17
|
||||
ultrahtml: 1.7.0
|
||||
unifont: 0.7.4
|
||||
unstorage: 1.17.5(@vercel/functions@3.9.1(ws@8.21.0))
|
||||
unstorage: 1.17.5(@vercel/functions@3.9.5(ws@8.21.0))
|
||||
vite: 8.1.3(@types/node@26.1.1)(esbuild@0.28.1)(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0)
|
||||
vitefu: 1.1.3(vite@8.1.3(@types/node@26.1.1)(esbuild@0.28.1)(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0))
|
||||
xxhash-wasm: 1.1.0
|
||||
@@ -7230,7 +7233,7 @@ snapshots:
|
||||
- uploadthing
|
||||
- yaml
|
||||
|
||||
astro@7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.1(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0):
|
||||
astro@7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(@vercel/functions@3.9.5(ws@8.21.0))(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0):
|
||||
dependencies:
|
||||
'@astrojs/compiler-rs': 0.3.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)
|
||||
'@astrojs/internal-helpers': 0.10.2
|
||||
@@ -7279,7 +7282,7 @@ snapshots:
|
||||
tinyglobby: 0.2.17
|
||||
ultrahtml: 1.7.0
|
||||
unifont: 0.7.4
|
||||
unstorage: 1.17.5(@vercel/functions@3.9.1(ws@8.21.0))
|
||||
unstorage: 1.17.5(@vercel/functions@3.9.5(ws@8.21.0))
|
||||
vite: 8.1.3(@types/node@26.1.1)(esbuild@0.28.1)(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0)
|
||||
vitefu: 1.1.3(vite@8.1.3(@types/node@26.1.1)(esbuild@0.28.1)(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0))
|
||||
xxhash-wasm: 1.1.0
|
||||
@@ -10568,7 +10571,7 @@ snapshots:
|
||||
|
||||
universalify@2.0.1: {}
|
||||
|
||||
unstorage@1.17.5(@vercel/functions@3.9.1(ws@8.21.0)):
|
||||
unstorage@1.17.5(@vercel/functions@3.9.5(ws@8.21.0)):
|
||||
dependencies:
|
||||
anymatch: 3.1.3
|
||||
chokidar: 5.0.0
|
||||
@@ -10579,7 +10582,7 @@ snapshots:
|
||||
ofetch: 1.5.1
|
||||
ufo: 1.6.4
|
||||
optionalDependencies:
|
||||
'@vercel/functions': 3.9.1(ws@8.21.0)
|
||||
'@vercel/functions': 3.9.5(ws@8.21.0)
|
||||
|
||||
update-browserslist-db@1.2.3(browserslist@4.28.7):
|
||||
dependencies:
|
||||
|
||||
Reference in new issue
Block a user