diff --git a/app/app.config.ts b/app/app.config.ts
index f2d8d00..5299153 100644
--- a/app/app.config.ts
+++ b/app/app.config.ts
@@ -185,7 +185,7 @@ export default defineAppConfig({
// info: 'i-tabler-info-square-rounded-filled',
},
},
- // `seo.siteName`, `header.title` and `github.*` are deliberately NOT defaulted here: modules/config.ts seeds
+ // `seo.siteName`, `header.title` and `github.*` are deliberately NOT defaulted here: modules/config/ seeds
// them into `nuxt.options.appConfig`, and app.config values — even empty strings — would win over those.
header: {
to: '/',
diff --git a/app/app.vue b/app/app.vue
index 90c6b72..3c9d7e6 100644
--- a/app/app.vue
+++ b/app/app.vue
@@ -1,6 +1,5 @@
+
+
+
+
+
+
diff --git a/app/components/AssistantChat.vue b/app/components/AssistantChat.vue
index 4adcc67..591c212 100644
--- a/app/components/AssistantChat.vue
+++ b/app/components/AssistantChat.vue
@@ -3,7 +3,7 @@ import { DefaultChatTransport, isReasoningUIPart, isTextUIPart, isToolUIPart, ge
import { useChat } from '@ai-sdk/vue'
import { isPartStreaming, isToolStreaming } from '@nuxt/ui/utils/ai'
import rangi from 'comark/plugins/rangi'
-import { geistTheme } from '../../utils/geist-theme'
+import { geistTheme } from '../../utils/geist'
const MAX_INPUT = 1000
diff --git a/app/components/landing/LandingHeroDemo.vue b/app/components/landing/LandingHeroDemo.vue
index da3aaef..96cddfa 100644
--- a/app/components/landing/LandingHeroDemo.vue
+++ b/app/components/landing/LandingHeroDemo.vue
@@ -1,6 +1,6 @@
@@ -32,11 +29,6 @@ provide('navigation', navigation)
-
-
-
+
diff --git a/app/utils/navigation.ts b/app/utils/navigation.ts
index 0cb746b..deaa0ab 100644
--- a/app/utils/navigation.ts
+++ b/app/utils/navigation.ts
@@ -23,7 +23,7 @@ function walk(items: NavigationItem[], path: string): boolean {
return false
}
-export { findFirstLeaf } from '../../utils/first-leaf'
+export { findFirstLeaf } from '../../utils/navigation'
export interface BreadcrumbItem {
title: string
diff --git a/app/utils/search-sections.ts b/app/utils/search-sections.ts
deleted file mode 100644
index 4aa65d9..0000000
--- a/app/utils/search-sections.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { defineContentClientPlugin } from 'comark-content/client'
-import { joinURL } from 'ufo'
-
-/** One search entry per document heading — consumed by `UContentSearch`. */
-export interface SearchSection {
- id: string
- title: string
- titles: string[]
- level: number
- content: string
-}
-
-interface SearchSectionsClientMethods {
- searchSections(): Promise
-}
-
-/** Client half of the `search-sections` serve handler (`server/utils/content.ts`); adds `content.searchSections()`. */
-export const searchSectionsClient = defineContentClientPlugin, SearchSectionsClientMethods>(() => ({
- name: 'search-sections',
- setup: ({ options }) => ({
- searchSections: () => options.fetch(joinURL(options.baseURL, options.basePath, 'search-sections')),
- }),
-}))
diff --git a/app/workers/internal/search-logger.ts b/app/workers/internal/search-logger.ts
new file mode 100644
index 0000000..64f4ee9
--- /dev/null
+++ b/app/workers/internal/search-logger.ts
@@ -0,0 +1,69 @@
+/**
+ * Logging for the search worker.
+ *
+ * Triggered by `?debug=search` param.
+ */
+import type { ContentFile, Logger, RelationalDatabase } from 'comark-content/runtime'
+
+const PREFIX = '[search:worker]'
+
+let debug = false
+
+/** Called on every `warmup`; once on, it stays on for the life of the worker. */
+export function setDebug(value: boolean): void {
+ debug = debug || value
+}
+
+export function isDebug(): boolean {
+ return debug
+}
+
+export function log(...args: unknown[]): void {
+ if (debug) console.info(PREFIX, ...args)
+}
+
+/** Milliseconds since `from`, for log lines. */
+export function since(from: number): string {
+ return `${(performance.now() - from).toFixed(1)}ms`
+}
+
+/**
+ * Warn and error are deliberately ungated: the FTS plugin reports a missing snapshot through this
+ * channel, and that failure is otherwise indistinguishable from "the query matched nothing".
+ */
+export const logger: Logger = {
+ debug: (tag, ...args) => log(`${tag}:`, ...args),
+ info: (tag, ...args) => log(`${tag}:`, ...args),
+ warn: (tag, ...args) => console.warn(`${PREFIX} ${tag}:`, ...args),
+ error: (tag, ...args) => console.error(`${PREFIX} ${tag}:`, ...args),
+}
+
+/**
+ * What a decoded artifact holds: a snapshot decodes to the source's items, the manifest to an object
+ * keyed by path. `with nodes` is the number that matters — the FTS plugin indexes
+ * `kind === 'document' && nodes?.length`, so a bodies-less (partial) snapshot builds an empty index.
+ */
+export function describeArtifact(decoded: unknown): string {
+ if (Array.isArray(decoded)) {
+ const items = decoded as ContentFile[]
+ const documents = items.filter((item) => item.meta.kind === 'document')
+ const withNodes = documents.filter((item) => item.nodes?.length)
+ return `${items.length} item(s), ${documents.length} document(s), ${withNodes.length} with nodes`
+ }
+ const items = (decoded as { items?: Record } | null)?.items
+ return `${items ? Object.keys(items).length : 0} manifest item(s)`
+}
+
+/**
+ * Rows in the FTS plugin's index — the one number that separates "nothing was indexed" from "the
+ * query found nothing", since `search()` catches SQL errors and returns `[]` either way. Reads the
+ * plugin's private table, so it is a diagnostic, not something to build on.
+ */
+export async function indexedRows(database: RelationalDatabase, source: string): Promise {
+ try {
+ const rows = await database.all<{ n: number }>('SELECT count(*) as n FROM __fts_search WHERE source = ?', [source])
+ return rows?.[0]?.n ?? 'unknown'
+ } catch (error) {
+ return `unknown (${error instanceof Error ? error.message : String(error)})`
+ }
+}
diff --git a/app/workers/search.ts b/app/workers/search.ts
new file mode 100644
index 0000000..e81e295
--- /dev/null
+++ b/app/workers/search.ts
@@ -0,0 +1,114 @@
+/**
+ * Search worker: owns the browser-standalone `comark-content` instance (sqlite-wasm FTS5).
+ *
+ * Hydrated from the per-commit snapshot artifacts.
+ */
+import { comarkContent, DEFAULT_CONTENT_NAME, readArtifact } from 'comark-content/runtime'
+import sqliteWasm from 'comark-content/database/sqlite-wasm'
+import snapshot from 'comark-content/sources/snapshot'
+import sqliteFullTextSearch from 'comark-content/plugins/sqlite-full-text-search'
+import { ofetch } from 'ofetch'
+import { describeArtifact, indexedRows, isDebug, log, logger, setDebug, since } from './internal/search-logger'
+import type { CacheArtifact, SearchOptions, SearchResult } from 'comark-content/runtime'
+
+/**
+ * Creates a search instance.
+ */
+function createSearchInstance(fetchArtifact: (path: string) => Promise, apiBase: string) {
+ const database = sqliteWasm()
+ return {
+ database,
+ content: comarkContent({
+ // The first (full-body) tier is what the index is built from; the second (manifest) tier
+ // is the light one `init()` prefers, so a bare `init()` below doesn't download bodies that
+ // `search()` is about to fetch anyway via the snapshot tier.
+ source: snapshot(
+ () => fetchArtifact(`${apiBase}/snapshot/${DEFAULT_CONTENT_NAME}.json`),
+ () => fetchArtifact(`${apiBase}/manifest.json`)
+ ),
+ plugins: [sqliteFullTextSearch({ database })],
+ logger,
+ }),
+ }
+}
+
+type SearchInstance = ReturnType['content']
+
+let instance: SearchInstance | undefined
+
+/**
+ * The in-flight hydration.
+ *
+ * Ensures only one hydration runs at a time.
+ */
+let hydration: Promise | undefined
+
+/** Loads the database. No-op once ready; retries after a failure. */
+export function warmupSearch(apiBase: string, origin: string, debug: boolean): Promise {
+ setDebug(debug)
+ if (instance) {
+ log('warmup ignored — already ready')
+ return Promise.resolve()
+ }
+ hydration ||= loadDatabase(apiBase, origin).catch((error) => {
+ hydration = undefined // clears the guard so the next warmup can retry
+ throw error
+ })
+ return hydration
+}
+
+async function loadDatabase(apiBase: string, origin: string): Promise {
+ const started = performance.now()
+ try {
+ const fetchArtifact = async (path: string): Promise => {
+ const url = new URL(path, origin).href
+ const fetchStarted = performance.now()
+ try {
+ const artifact = await ofetch(url)
+ if (isDebug()) {
+ let contents: string
+ try {
+ contents = describeArtifact(await readArtifact(artifact))
+ } catch (error) {
+ contents = `undecodable: ${error instanceof Error ? error.message : String(error)}`
+ }
+ log(`fetched ${path} in ${since(fetchStarted)} — ${artifact?.size ?? 0} bytes, ${contents}`)
+ }
+ return artifact
+ } catch (error) {
+ log(`failed ${path} after ${since(fetchStarted)}`, error)
+ throw error
+ }
+ }
+
+ const { database, content } = createSearchInstance(fetchArtifact, apiBase)
+
+ await content.init()
+
+ const indexStarted = performance.now()
+ await content.search('') // pulls the snapshot in and builds the FTS index
+ log(`index built in ${since(indexStarted)} — ${await indexedRows(database, DEFAULT_CONTENT_NAME)} row(s)`)
+
+ instance = content
+ log(`ready in ${since(started)}`)
+ } catch (error) {
+ log(`hydration failed after ${since(started)}`, error)
+ throw error
+ }
+}
+
+/** Empty until hydration lands. */
+export async function searchContent(query: string, opts?: SearchOptions): Promise {
+ if (!instance) {
+ log(`dropped query "${query}" — no instance yet`)
+ return []
+ }
+ const queryStarted = performance.now()
+ const results = await instance.search(query, {
+ limit: 25,
+ snippet: { columns: ['content'] },
+ ...opts,
+ })
+ log(`query "${query}" -> ${results.length} result(s) in ${since(queryStarted)}`)
+ return results
+}
diff --git a/docs/cold-page-request.md b/docs/cold-page-request.md
index b75ca41..98fa4f7 100644
--- a/docs/cold-page-request.md
+++ b/docs/cold-page-request.md
@@ -9,7 +9,7 @@ sequenceDiagram
participant SSR as Lambda SSR
participant ContentRoute as /api/content/**
participant Config as Vercel Global Config
- participant Refs as Shared ref cache (content:refs)
+ participant Refs as Shared ref cache (content:refs:v2)
participant GH as GitHub
participant Content as shared content
@@ -24,7 +24,7 @@ sequenceDiagram
else no pin or read failed
Config-->>Content: undefined
Content->>Refs: resolveContentSha(targetBranch, contentDir)
- alt cache hit (within 60s TTL)
+ alt cache hit (within 1h fallback TTL)
Refs-->>Content: cached content sha
else cache miss
Refs->>GH: commits?sha=&path=
@@ -51,13 +51,20 @@ its index from GitHub once per content revision, then parses one page. All reads
immutable ``. Without a Global Config pin, code-only commits do not rebuild the content
instance.
-The ref cache is shared across *instances*, so GitHub is hit once per 60s TTL window
-rather than once per cold start. It is **not** shared across regions — Vercel's
-Runtime Cache is regional (see the note on `refCacheDriver()` in
-`server/utils/cache.ts`), so the ceiling is one GitHub call per region per window.
-This project runs single-region, which is what makes that distinction academic today.
+The production branch pointer is shared across *instances* with a one-hour fallback TTL. The push
+webhook refreshes it before purging ISR, so normal cold starts don't need to resolve the branch
+through GitHub. If a webhook delivery or refresh fails, a request resolves the branch again after
+the fallback TTL instead of serving the old SHA indefinitely.
-A ref that doesn't resolve is cached too, for the same window, but **only** when the
+Vercel's Runtime Cache is regional (see the note on `refCacheDriver()` in
+`server/utils/cache.ts`). This project runs in one region, so the webhook refresh reaches every
+instance's shared cache. Additional regions would self-heal when their fallback TTL expires.
+
+Preview deployments, `/tree/:branch`, `/pr/:number`, and preview authorization decisions use a
+600-second TTL because production webhooks don't update them. Negative ref lookups use the same TTL,
+including for the production branch, so a temporary GitHub 404 cannot remain cached indefinitely.
+
+A ref that doesn't resolve is cached too, for 600 seconds, but **only** when the
caller asks for it (`resolveContentSha(ref, contentDir, { cacheMisses: true })`) — the public
`/tree/:branch` route does, so a nonexistent branch can't be replayed into one
GitHub API call per request. The production branch above deliberately does not:
@@ -68,8 +75,9 @@ failed request.
**On a content push**, `server/api/revalidate.post.ts` forces a fresh `resolveContentSha()` lookup,
which writes the latest branch content SHA into the same shared ref cache before fanning out ISR
purges for the affected pages. Without a Global Config pin, a freshly-purged page's next render sees
-the new SHA instead of waiting out the 60-second TTL. With a pin, the next render stays on the pinned
-SHA, while the refreshed branch pointer is ready if the pin is removed.
+the new SHA. With a pin, the next render stays on the pinned SHA, while the refreshed branch pointer
+is ready if the pin is removed. If the webhook fails, the production pointer refreshes within one
+hour.
Parsed manifests and bodies live under a parser-version + content-SHA namespace. Vercel
Runtime Cache persists across deployments within an environment, so unrelated deployments can reuse
diff --git a/modules/config.ts b/modules/config/index.ts
similarity index 90%
rename from modules/config.ts
rename to modules/config/index.ts
index 8af5410..de9402d 100644
--- a/modules/config.ts
+++ b/modules/config/index.ts
@@ -2,10 +2,8 @@ import { existsSync, readdirSync } from 'node:fs'
import { addServerPlugin, createResolver, defineNuxtModule, useLogger } from '@nuxt/kit'
import { defu } from 'defu'
import type { ModuleOptions as AgentDiscoveryOptions } from 'nuxt-agent-discovery'
-import { resolveContentDir } from '../utils/content-dir'
-import { getGitBranch, getGitEnv, getGitRoot, getLocalGitInfo } from '../utils/git'
-import { LAYER_ICON_COLLECTIONS } from '../utils/icons'
-import { getPackageJsonMetadata, inferSiteURL } from '../utils/meta'
+import { getGitBranch, getGitEnv, getGitRoot, getLocalGitInfo } from '../../utils/git'
+import { getPackageJsonMetadata, inferSiteURL, resolveContentDir } from './utils'
const logger = useLogger('comark-docs')
@@ -138,16 +136,6 @@ export default defineNuxtModule({
},
})
- // Drop layer Iconify prefixes from appConfig so @nuxt/icon keeps using the Iconify API (not `/api/_nuxt_icon`).
- nuxt.hook('modules:done', () => {
- const iconAppConfig = nuxtOptions.appConfig.icon as { customCollections?: string[] } | undefined
- if (!iconAppConfig?.customCollections?.length) return
- iconAppConfig.customCollections = iconAppConfig.customCollections.filter(
- (prefix) => !LAYER_ICON_COLLECTIONS.includes(prefix)
- )
- })
-
-
// Extend Nuxt UI components to make them global and usable in markdown by consumers.
nuxt.hook('components:extend', (components) => {
const globalComponents = ['UButton', 'UPageHero']
@@ -249,7 +237,7 @@ export default defineNuxtModule({
// Previews are served live (SSR) off Runtime Cache; `/blob/**` is immutable commit HTML.
// `/pr/**` follows the PR's head like `/tree/**` follows a branch, so it shares the short TTL.
'/tree/**': { isr, robots: 'noindex, nofollow' },
- '/blob/**': { isr: true, robots: 'noindex, nofollow' },
+ '/blob/**': { isr: true, robots: 'noindex, nofollow' }, // Immutable since SHA-pinned
'/pr/**': { isr, robots: 'noindex, nofollow' },
// Raw markdown mirrors of every page, for agents.
'/raw/**': { isr, robots: 'noindex' },
@@ -260,11 +248,17 @@ export default defineNuxtModule({
'/rss.xml': { isr },
// Prerendering would bake the build-time site URL and `docs.version` into it.
'/openapi.json': { isr },
- // Fetched on every page hydration (see app.vue) and parses every doc body, so cache it.
- '/api/content/blob/*/search-sections': { isr: true },
- '/api/content/tree/*/search-sections': { isr },
- '/api/content/pr/*/search-sections': { isr },
- '/api/content/search-sections': { isr },
+ // Scanned from the app at build time, so they only change on deploy.
+ '/.well-known/skills': { isr: true },
+ '/.well-known/skills/**': { isr: true },
+ // Per-commit artifacts hydrating the client-side search database (see `useSearch`)
+ '/api/content/blob/*/manifest.json': { isr: true }, // Immutable since SHA-pinned
+ '/api/content/blob/*/snapshot/*': { isr: true }, // Immutable since SHA-pinned
+ '/api/content/tree/*/manifest.json': { isr },
+ '/api/content/tree/*/snapshot/*': { isr },
+ // `/pr/*` follows the PR head, so it gets the short TTL like `/tree/*`.
+ '/api/content/pr/*/manifest.json': { isr },
+ '/api/content/pr/*/snapshot/*': { isr },
'/api/code-explorer/**': { isr },
'/_payload.json': {
headers: { 'cache-control': `public, max-age=${isr}, s-maxage=${isr}, stale-while-revalidate=60` },
@@ -286,6 +280,15 @@ export default defineNuxtModule({
// Consumer-declared rules win per route.
nuxt.options.routeRules = defu(nuxt.options.routeRules, rules) as typeof nuxt.options.routeRules
+
+ // Remove once https://github.com/benjamincanac/nuxt-agent-discovery/pull/35 is released.
+ nuxt.hook('modules:done', () => {
+ nuxt.hook('prerender:routes', (ctx) => {
+ for (const route of ctx.routes) {
+ if (route.startsWith('/.well-known/skills')) ctx.routes.delete(route)
+ }
+ })
+ })
}
},
})
diff --git a/modules/runtime/server/plugins/llms.ts b/modules/config/runtime/server/plugins/llms.ts
similarity index 100%
rename from modules/runtime/server/plugins/llms.ts
rename to modules/config/runtime/server/plugins/llms.ts
diff --git a/test/content-dir.test.ts b/modules/config/test/config.test.ts
similarity index 60%
rename from test/content-dir.test.ts
rename to modules/config/test/config.test.ts
index 993b7cf..be00c54 100644
--- a/test/content-dir.test.ts
+++ b/modules/config/test/config.test.ts
@@ -1,5 +1,5 @@
-import { describe, expect, it } from 'vitest'
-import { resolveContentDir } from '../utils/content-dir'
+import { afterEach, beforeEach, describe, expect, it } from 'vitest'
+import { inferSiteURL, resolveContentDir } from '../utils'
describe('resolveContentDir', () => {
it('relativises against the git root for an app in a subdirectory', () => {
@@ -64,3 +64,54 @@ describe('resolveContentDir', () => {
})
})
})
+
+describe('inferSiteURL', () => {
+ const keys = [
+ 'NUXT_PUBLIC_SITE_URL',
+ 'NUXT_SITE_URL',
+ 'VERCEL_PROJECT_PRODUCTION_URL',
+ 'VERCEL_BRANCH_URL',
+ 'VERCEL_URL',
+ 'URL',
+ 'CI_PAGES_URL',
+ 'CF_PAGES_URL',
+ ]
+ let saved: Record
+
+ // `Reflect.deleteProperty` rather than `delete process.env[key]`: same effect,
+ // without tripping `no-dynamic-delete`.
+ const unset = (key: string) => Reflect.deleteProperty(process.env, key)
+
+ beforeEach(() => {
+ saved = Object.fromEntries(keys.map((key) => [key, process.env[key]]))
+ for (const key of keys) unset(key)
+ })
+
+ afterEach(() => {
+ for (const [key, value] of Object.entries(saved)) {
+ if (value === undefined) unset(key)
+ else process.env[key] = value
+ }
+ })
+
+ it('returns undefined when nothing is set', () => {
+ expect(inferSiteURL()).toBeUndefined()
+ })
+
+ it('adds https to a bare Vercel host', () => {
+ process.env.VERCEL_URL = 'my-app-abc123.vercel.app'
+ expect(inferSiteURL()).toBe('https://my-app-abc123.vercel.app')
+ })
+
+ it('prefers the explicit override over the platform value', () => {
+ process.env.VERCEL_URL = 'my-app-abc123.vercel.app'
+ process.env.NUXT_PUBLIC_SITE_URL = 'https://docs.example.com'
+ expect(inferSiteURL()).toBe('https://docs.example.com')
+ })
+
+ it('prefers the production URL over the per-branch one', () => {
+ process.env.VERCEL_BRANCH_URL = 'branch.vercel.app'
+ process.env.VERCEL_PROJECT_PRODUCTION_URL = 'docs.comark.dev'
+ expect(inferSiteURL()).toBe('https://docs.comark.dev')
+ })
+})
diff --git a/utils/content-dir.ts b/modules/config/utils.ts
similarity index 59%
rename from utils/content-dir.ts
rename to modules/config/utils.ts
index 1dcc9f1..4648e48 100644
--- a/utils/content-dir.ts
+++ b/modules/config/utils.ts
@@ -1,4 +1,6 @@
-import { join, normalize, relative } from 'pathe'
+import { readFile } from 'node:fs/promises'
+import { join, normalize, relative, resolve } from 'pathe'
+import { withHttps } from 'ufo'
export interface ContentDirInput {
rootDir: string
@@ -37,3 +39,30 @@ export function resolveContentDir({ rootDir, gitRoot, explicit }: ContentDirInpu
return { contentPath, contentDir: 'content', source: 'assumed' }
}
+
+/** Infer the public site URL from the deployment platform env. */
+export function inferSiteURL(): string | undefined {
+ // https://github.com/unjs/std-env/issues/59
+ const url =
+ process.env.NUXT_PUBLIC_SITE_URL ||
+ process.env.NUXT_SITE_URL ||
+ process.env.VERCEL_PROJECT_PRODUCTION_URL ||
+ process.env.VERCEL_BRANCH_URL ||
+ process.env.VERCEL_URL ||
+ process.env.URL || // Netlify
+ process.env.CI_PAGES_URL || // GitLab Pages
+ process.env.CF_PAGES_URL // Cloudflare Pages
+
+ return url ? withHttps(url) : undefined
+}
+
+export async function getPackageJsonMetadata(
+ dir: string
+): Promise<{ name?: string; description?: string; version?: string }> {
+ try {
+ const parsed = JSON.parse(await readFile(resolve(dir, 'package.json'), 'utf-8'))
+ return { name: parsed.name, description: parsed.description, version: parsed.version }
+ } catch {
+ return {}
+ }
+}
diff --git a/modules/snapshot/index.ts b/modules/snapshot/index.ts
new file mode 100644
index 0000000..ce2b70f
--- /dev/null
+++ b/modules/snapshot/index.ts
@@ -0,0 +1,78 @@
+import { mkdir, stat } from 'node:fs/promises'
+import { defineNuxtModule, useLogger } from '@nuxt/kit'
+import { DEFAULT_CONTENT_NAME } from 'comark-content'
+import { writeSnapshots } from 'comark-content/build'
+import fs from 'comark-content/sources/fs'
+import { join } from 'pathe'
+import { createBuildContentInstance } from '../../utils/content'
+import { resolveSnapshotSha } from './utils'
+
+const logger = useLogger('comark-docs')
+
+/** Where the snapshot lives in the build, and the server-asset namespace it is read back through. */
+const ASSET_BASE = 'comark-content'
+
+/**
+ * Writes a build-time snapshot into the function bundle stamped with the commit it was parsed at.
+ * A cold start at that commit hydrates from it instead of walking the content repository.
+ * At a later commit it still supplies every unchanged body.
+ */
+export default defineNuxtModule({
+ meta: { name: 'comark-docs:snapshot' },
+ setup(_options, nuxt) {
+ // Do not run in dev or prepare.
+ if (nuxt.options.dev || nuxt.options._prepare) return
+
+ const dir = join(nuxt.options.buildDir, ASSET_BASE)
+
+ nuxt.hook('modules:done', async () => {
+ await mkdir(dir, { recursive: true })
+ nuxt.options.nitro.serverAssets = [
+ ...(nuxt.options.nitro.serverAssets ?? []),
+ { baseName: ASSET_BASE, dir },
+ ]
+ })
+
+ nuxt.hook('build:before', async () => {
+ const { docs } = nuxt.options.runtimeConfig
+ const { repoRoot, contentDir, contentPath, github } = docs
+
+ const resolveStart = performance.now()
+ const sha = await resolveSnapshotSha({
+ repoRoot,
+ contentDir,
+ repo: `${github.owner}/${github.repo}`,
+ token: docs.githubToken || process.env.NUXT_DOCS_GITHUB_TOKEN || process.env.GITHUB_TOKEN,
+ warn: (message) => logger.warn(message),
+ })
+ const resolveMs = Math.round(performance.now() - resolveStart)
+ if (!sha) {
+ logger.warn(
+ 'No commit in this checkout could be confirmed to hold the content being built, ' +
+ 'so no snapshot is shipped: cold starts will walk the content repository.'
+ )
+ return
+ }
+
+ // `withRef` stamps the artifact with the commit.
+ // At runtime, even with a different commit, we can reuse unchanged bodies.
+ const content = createBuildContentInstance({ source: fs(contentPath) }).withRef(sha)
+
+ try {
+ const writeStart = performance.now()
+ await writeSnapshots(content, { dir })
+ const writeMs = Math.round(performance.now() - writeStart)
+
+ // Size is the number to watch: the snapshot is inlined into the bundle as a string.
+ // Every cold start that reads it pays for that.
+ const { size } = await stat(join(dir, DEFAULT_CONTENT_NAME, 'snapshot.json'))
+ logger.success(
+ `Content snapshot ${sha.slice(0, 7)}: ${Math.round(size / 1024)} kB parsed and written in ${writeMs}ms ` +
+ `(ref resolved in ${resolveMs}ms)`
+ )
+ } catch (error) {
+ logger.warn('Could not write the content snapshot — cold starts will walk the content repository.', error)
+ }
+ })
+ },
+})
diff --git a/modules/snapshot/test/snapshot.test.ts b/modules/snapshot/test/snapshot.test.ts
new file mode 100644
index 0000000..65cd868
--- /dev/null
+++ b/modules/snapshot/test/snapshot.test.ts
@@ -0,0 +1,116 @@
+import { execFileSync } from 'node:child_process'
+import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'
+import { tmpdir } from 'node:os'
+import { dirname, join } from 'node:path'
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
+import { resolveSnapshotSha } from '../utils'
+
+const SHA = (char: string) => char.repeat(40)
+
+describe('resolveSnapshotSha', () => {
+ let repo: string
+ let contentCommit: string
+ let head: string
+
+ const run = (...args: string[]) =>
+ execFileSync('git', args, { cwd: repo, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
+
+ const write = async (file: string, body: string) => {
+ await mkdir(dirname(join(repo, file)), { recursive: true })
+ await writeFile(join(repo, file), body, 'utf8')
+ }
+
+ /** Answers the commits query with `sha` per requested ref; `null` means 404. */
+ function stubApi(bySha: Record) {
+ return vi.fn(async (url: string | URL) => {
+ const ref = new URL(String(url)).searchParams.get('sha') ?? ''
+ const answer = bySha[ref]
+ if (answer === undefined || answer === null) return new Response('[]', { status: 404 })
+ return new Response(JSON.stringify([{ sha: answer }]), { status: 200 })
+ })
+ }
+
+ beforeEach(async () => {
+ repo = await mkdtemp(join(tmpdir(), 'comark-snapshot-sha-'))
+ run('init', '-q', '-b', 'main')
+ run('config', 'user.email', 'test@example.com')
+ run('config', 'user.name', 'Test')
+
+ await write('content/index.md', '# one\n')
+ run('add', '-A')
+ run('commit', '-qm', 'add content')
+ contentCommit = run('rev-parse', 'HEAD')
+
+ // A later commit that leaves `content/` alone, so HEAD is not the last content commit.
+ await write('src/app.ts', 'export const a = 1\n')
+ run('add', '-A')
+ run('commit', '-qm', 'add code')
+ head = run('rev-parse', 'HEAD')
+ })
+
+ afterEach(async () => {
+ await rm(repo, { recursive: true, force: true })
+ vi.unstubAllGlobals()
+ })
+
+ const input = () => ({ repoRoot: repo, contentDir: 'content', repo: 'owner/name', token: 'tok' })
+
+ it('walks from the built commit, not the branch', async () => {
+ // The distinction that keeps a mid-build push (or a redeploy of an older commit) from labelling
+ // the snapshot with content it does not hold.
+ const fetchMock = stubApi({ [head]: contentCommit, main: SHA('f') })
+ vi.stubGlobal('fetch', fetchMock)
+
+ expect(await resolveSnapshotSha(input())).toBe(contentCommit)
+
+ const requested = new URL(String(fetchMock.mock.calls[0]![0])).searchParams
+ expect(requested.get('sha')).toBe(head)
+ expect(requested.get('path')).toBe('content')
+ expect(requested.get('per_page')).toBe('1')
+ })
+
+ it('falls back to a tree-verified git answer when the API fails', async () => {
+ vi.stubGlobal('fetch', stubApi({}))
+
+ // Full history here, so git finds the true commit and its content tree matches HEAD's.
+ expect(await resolveSnapshotSha(input())).toBe(contentCommit)
+ })
+
+ it('skips the API when no repository is known', async () => {
+ const fetchMock = stubApi({ [head]: SHA('c') })
+ vi.stubGlobal('fetch', fetchMock)
+
+ expect(await resolveSnapshotSha({ ...input(), repo: '' })).toBe(contentCommit)
+ expect(fetchMock).not.toHaveBeenCalled()
+ })
+
+ it('ships nothing when neither the API nor git can name the content', async () => {
+ vi.stubGlobal('fetch', stubApi({}))
+
+ expect(await resolveSnapshotSha({ ...input(), contentDir: 'nope' })).toBeUndefined()
+ })
+
+ it('warns on the git fallback when the answer is a shallow boundary', async () => {
+ vi.stubGlobal('fetch', stubApi({}))
+ const warn = vi.fn()
+
+ // A one-commit repo: its only commit is parentless, which is what a depth-1 clone looks like.
+ const shallow = await mkdtemp(join(tmpdir(), 'comark-shallow-'))
+ try {
+ const at = (...args: string[]) => execFileSync('git', args, { cwd: shallow, stdio: 'ignore' })
+ at('init', '-q', '-b', 'main')
+ at('config', 'user.email', 'test@example.com')
+ at('config', 'user.name', 'Test')
+ await mkdir(join(shallow, 'content'), { recursive: true })
+ await writeFile(join(shallow, 'content/index.md'), '# one\n', 'utf8')
+ at('add', '-A')
+ at('commit', '-qm', 'init')
+
+ expect(await resolveSnapshotSha({ ...input(), repoRoot: shallow, warn })).toMatch(/^[0-9a-f]{40}$/)
+ expect(warn).toHaveBeenCalledOnce()
+ expect(warn.mock.calls[0]![0]).toContain('shallow clone boundary')
+ } finally {
+ await rm(shallow, { recursive: true, force: true })
+ }
+ })
+})
diff --git a/modules/snapshot/utils.ts b/modules/snapshot/utils.ts
new file mode 100644
index 0000000..af03c95
--- /dev/null
+++ b/modules/snapshot/utils.ts
@@ -0,0 +1,62 @@
+import { getLastCommit, getTreeSha, hasParent, headCommit } from '../../utils/git'
+import { fetchLastContentCommit } from '../../utils/github'
+
+export interface SnapshotShaInput {
+ /** Repository root of the checkout being built. */
+ repoRoot: string
+ /** Content directory, relative to the repository root. */
+ contentDir: string
+ /** `owner/name` of the content repository. Empty when the checkout has no usable remote. */
+ repo: string
+ /** GitHub token, if the build has one. Without it only the git fallback runs. */
+ token?: string
+ /** Reported to the caller; defaults to `console.warn`. */
+ warn?: (message: string) => void
+}
+
+/** {@link fetchLastContentCommit}, but never throwing: a build-time optimization must not fail a build. */
+async function lastContentCommit(
+ repo: string,
+ contentDir: string,
+ ref: string,
+ token?: string
+): Promise {
+ try {
+ const sha = await fetchLastContentCommit({ repo, path: contentDir, ref, token })
+ // Validated here rather than in the shared query: this one names a directory in the build.
+ return sha && /^[0-9a-f]{40}$/.test(sha) ? sha : undefined
+ } catch {
+ return undefined
+ }
+}
+
+/**
+ * The commit whose `contentDir` holds the content being parsed.
+ * The only ref the snapshot may be stored under.
+ * The same one `resolveContentSha()` resolves at runtime.
+ */
+export async function resolveSnapshotSha(input: SnapshotShaInput): Promise {
+ const { repoRoot, contentDir, repo, token } = input
+ const warn = input.warn ?? ((message: string) => console.warn(message))
+
+ const head = headCommit(repoRoot)
+ const fromApi = head && repo ? await lastContentCommit(repo, contentDir, head, token) : undefined
+ if (fromApi) return fromApi
+
+ // No API answer: fall back to git, which needs the tree check to be trustworthy.
+ const parsed = getTreeSha(repoRoot, 'HEAD', contentDir)
+ const fromGit = getLastCommit(repoRoot, contentDir)
+ if (!parsed || !fromGit) return undefined
+
+ if (getTreeSha(repoRoot, fromGit, contentDir) !== parsed) return undefined
+
+ if (!hasParent(repoRoot, fromGit)) {
+ warn(
+ `Could not reach the GitHub API, and git labels the snapshot ${fromGit.slice(0, 7)}, ` +
+ `which has no parent in this checkout — a shallow clone boundary.\n` +
+ ` The snapshot is safe, but probably will not be looked up under that commit at runtime.`
+ )
+ }
+
+ return fromGit
+}
diff --git a/nuxt.config.ts b/nuxt.config.ts
index de6f8c8..3a9397d 100644
--- a/nuxt.config.ts
+++ b/nuxt.config.ts
@@ -1,7 +1,7 @@
import { resolveModulePath } from 'exsolve'
import { defineNuxtConfig } from 'nuxt/config'
import { createResolver } from 'nuxt/kit'
-import { layerIconCollections } from './utils/icons'
+import { LAYER_ICON_COLLECTIONS, layerIconAliases } from './utils/icons'
const { resolve } = createResolver(import.meta.url)
@@ -12,7 +12,7 @@ export default defineNuxtConfig({
// The layer's own modules go first: what config.ts seeds (`site`, `mcp`, `agentDiscovery`) has to be in
// place before the modules that read it at setup. Nuxt queues a layer's `modules` before its scanned
// `modules/` dir and dedupes by file path, so the extension is what keeps these from installing twice.
- resolve('./modules/config.ts'),
+ resolve('./modules/config/index.ts'),
resolve('./modules/css.ts'),
'@nuxt/ui',
'@comark/nuxt',
@@ -23,6 +23,7 @@ export default defineNuxtConfig({
'nuxt-schema-org',
'@nuxtjs/mcp-toolkit',
'nuxt-llms',
+ 'nuxt-workers',
'nuxt-agent-discovery',
],
ignore: ['content/**'],
@@ -52,29 +53,31 @@ export default defineNuxtConfig({
},
ogImage: { zeroRuntime: false },
icon: {
- provider: 'iconify',
- customCollections: layerIconCollections() as never,
- clientBundle: {
- scan: true,
- includeCustomCollections: false
- },
+ provider: 'server',
+ fallbackToApi: 'client-only',
+ serverBundle: { collections: LAYER_ICON_COLLECTIONS },
+ clientBundle: { scan: true },
},
vite: {
resolve: {
alias: { 'beautiful-mermaid': resolveModulePath('beautiful-mermaid', { from: import.meta.url }) },
},
+ worker: { format: 'es' },
optimizeDeps: {
include: [
'beautiful-mermaid',
'comark-docs > ai > @ai-sdk/gateway > @vercel/oidc',
'js-yaml'
],
+ // Pre-bundling would break the wasm/worker assets sqlite loads relative to its module URL.
+ exclude: ['@sqlite.org/sqlite-wasm'],
},
},
llms: {
prerender: false,
},
nitro: {
+ alias: layerIconAliases(),
// MCP tool handlers reach the request through `useEvent()`.
experimental: { asyncContext: true },
vercel: {
diff --git a/package.json b/package.json
index 486433a..ffb09ea 100644
--- a/package.json
+++ b/package.json
@@ -38,12 +38,14 @@
"dev:prepare": "nuxt prepare playground"
},
"dependencies": {
- "@ai-sdk/gateway": "^4.0.62",
- "@ai-sdk/vue": "^4.0.77",
+ "@ai-sdk/gateway": "^4.0.78",
+ "@ai-sdk/vue": "^4.0.97",
"@comark/nuxt": "^0.6.2",
- "@iconify-json/lucide": "^1.2.125",
- "@iconify-json/simple-icons": "^1.2.93",
- "@iconify-json/vscode-icons": "^1.2.74",
+ "@iconify-json/logos": "^1.2.14",
+ "@iconify-json/lucide": "^1.2.131",
+ "@iconify-json/simple-icons": "^1.2.95",
+ "@iconify-json/unjs": "^1.2.4",
+ "@iconify-json/vscode-icons": "^1.2.77",
"@iconify/vue": "^5.0.1",
"@nuxt/kit": "^4.5.2",
"@nuxt/ui": "^4.11.1",
@@ -53,32 +55,34 @@
"@octokit/webhooks-methods": "^6.0.0",
"@opentelemetry/api": "^1.9.1",
"@resvg/resvg-js": "^2.6.2",
+ "@sqlite.org/sqlite-wasm": "3.53.0-build1",
"@vercel/analytics": "^2.0.1",
- "@vercel/functions": "^3.9.5",
+ "@vercel/functions": "^3.9.7",
"@vercel/global-config": "^1.5.1",
"@vercel/otel": "^2.1.3",
"@vercel/speed-insights": "^2.0.0",
"@vueuse/core": "^14.4.0",
- "ai": "^7.0.77",
+ "ai": "^7.0.97",
"beautiful-mermaid": "^1.1.3",
"comark": "^0.6.2",
- "comark-content": "https://pkg.pr.new/comark-content@baefd4d",
+ "comark-content": "^0.4.0",
"defu": "^6.1.7",
"exsolve": "^1.1.1",
- "js-yaml": "^5.3.0",
- "motion-v": "^2.4.0",
+ "js-yaml": "^5.4.1",
+ "motion-v": "^2.4.2",
"nuxt-agent-discovery": "^0.5.1",
"nuxt-llms": "https://pkg.pr.new/nuxt-content/nuxt-llms/nuxt-llms@f6a9730",
"nuxt-og-image": "^6.7.8",
"nuxt-schema-org": "^6.3.1",
- "nuxt-seo-utils": "^8.4.2",
+ "nuxt-seo-utils": "^8.5.0",
+ "nuxt-workers": "^0.1.0",
"pathe": "^2.0.3",
"rangi": "^2.2.0",
"satori": "^0.29.1",
"tailwindcss": "^4.3.3",
"ufo": "^1.6.4",
"unstorage": "^1.17.5",
- "zod": "^4.4.3"
+ "zod": "^4.6.1"
},
"peerDependencies": {
"nuxt": "^4.5.0"
@@ -87,10 +91,10 @@
"@nuxt/devtools-kit": "4.0.0-alpha.9",
"@nuxt/eslint-config": "^1.17.0",
"@opentelemetry/exporter-trace-otlp-proto": "^0.221.0",
- "@opentelemetry/resources": "^2.10.0",
- "@opentelemetry/sdk-trace-base": "^2.10.0",
- "@opentelemetry/sdk-trace-node": "^2.10.0",
- "eslint": "^10.9.0",
+ "@opentelemetry/resources": "^2.11.0",
+ "@opentelemetry/sdk-trace-base": "^2.11.0",
+ "@opentelemetry/sdk-trace-node": "^2.11.0",
+ "eslint": "^10.10.0",
"nuxt": "^4.5.2",
"typescript": "^6.0.3",
"vitest": "^4.1.11",
diff --git a/playground/content/3.concepts/1.architecture.md b/playground/content/3.concepts/1.architecture.md
index adf5c82..8cad4f3 100644
--- a/playground/content/3.concepts/1.architecture.md
+++ b/playground/content/3.concepts/1.architecture.md
@@ -23,7 +23,7 @@ In development, none of this applies: content is read straight from your working
Production doesn't read "whatever is on `main` right now." On each server render, comark-docs first checks a connected Vercel Global Config store for a `contentSha` value. When the value exists, every production content read is pinned to that commit. You can use this override to hold production on a reviewed version or roll content back without changing the production branch. See [Pin production content](/deployment/vercel#pin-production-content) for setup and cache timing.
-Without a `contentSha` value, the server resolves the latest commit **touching the content directory** on the production branch. A shared, 60-second-TTL cache keeps this to about one GitHub call per minute. If Global Config is unavailable, comark-docs also falls back to this branch-based resolution.
+Without a `contentSha` value, the server resolves the latest commit **touching the content directory** on the production branch. The push webhook refreshes the shared pointer before purging affected pages, and a one-hour fallback TTL bounds staleness if the webhook fails. If Global Config is unavailable, comark-docs also falls back to this branch-based resolution.
The Global Config pin applies only to the production Vercel environment. Preview deployments continue to follow their target branch, and local development reads from your working tree.
@@ -69,7 +69,7 @@ sequenceDiagram
The handler verifies the webhook signature with `WEBHOOK_SECRET`, resolves the new content SHA, diffs the file manifests to find affected pages, and purges exactly those from the ISR cache. The next request renders from the new commit — typically live within seconds of the push.
-Without the webhook, the site still updates: ISR entries expire on their own after the `isr` window. The webhook just makes it immediate.
+The webhook advances the production pointer immediately. If delivery or refresh fails, the pointer expires within one hour and the next server render resolves the latest content commit through GitHub.
## Markdown for agents
diff --git a/playground/content/3.concepts/2.versioned-previews.md b/playground/content/3.concepts/2.versioned-previews.md
index d054546..91ba73e 100644
--- a/playground/content/3.concepts/2.versioned-previews.md
+++ b/playground/content/3.concepts/2.versioned-previews.md
@@ -29,7 +29,7 @@ So a commit only renders under `/blob/:sha` when at least one of these holds:
- A pull request from your own repository contains it. Contributors with push access could publish a `/tree/` preview anyway, so their PRs need no extra step.
- A pull request from a fork contains it **and** a maintainer added the `preview:enabled` label to that PR.
-`/pr/:number` follows the same rule: same-repo PRs always render, fork PRs only with the `preview:enabled` label. Removing the label revokes both within about a minute (the decision cache's TTL).
+`/pr/:number` follows the same rule: same-repo PRs always render, fork PRs only with the `preview:enabled` label. Removing the label revokes both within 10 minutes (the decision cache's TTL).
Every other SHA answers 404, and `/tree/` rejects GitHub's hidden `pull//head` refs, so the label check can't be sidestepped through a branch preview.
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 03ca933..2601cde 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -12,23 +12,29 @@ importers:
.:
dependencies:
'@ai-sdk/gateway':
- specifier: ^4.0.62
- version: 4.0.62(zod@4.4.3)
+ specifier: ^4.0.78
+ version: 4.0.78(zod@4.6.1)
'@ai-sdk/vue':
- specifier: ^4.0.77
- version: 4.0.77(vue@3.5.41(typescript@6.0.3))(zod@4.4.3)
+ specifier: ^4.0.97
+ version: 4.0.97(vue@3.5.41(typescript@6.0.3))(zod@4.6.1)
'@comark/nuxt':
specifier: ^0.6.2
- version: 0.6.2(a7af4bed794ccaa8b491cb4661979ebb)
+ version: 0.6.2(d45826d7a399b3ded44dd0ff86d4ffd7)
+ '@iconify-json/logos':
+ specifier: ^1.2.14
+ version: 1.2.14
'@iconify-json/lucide':
- specifier: ^1.2.125
- version: 1.2.125
+ specifier: ^1.2.131
+ version: 1.2.131
'@iconify-json/simple-icons':
- specifier: ^1.2.93
- version: 1.2.93
+ specifier: ^1.2.95
+ version: 1.2.95
+ '@iconify-json/unjs':
+ specifier: ^1.2.4
+ version: 1.2.4
'@iconify-json/vscode-icons':
- specifier: ^1.2.74
- version: 1.2.74
+ specifier: ^1.2.77
+ version: 1.2.77
'@iconify/vue':
specifier: ^5.0.1
version: 5.0.1(vue@3.5.41(typescript@6.0.3))
@@ -37,16 +43,16 @@ importers:
version: 4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))
'@nuxt/ui':
specifier: ^4.11.1
- version: 4.11.1(0716ad8e1ed9af83500c5a16dcdaaa58)
+ version: 4.11.1(f9711837b53e5eed9eacf00ad56e4c40)
'@nuxtjs/mcp-toolkit':
specifier: ^0.18.1
- version: 0.18.1(@vue/compiler-sfc@3.5.41)(h3@1.15.11)(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup@4.62.5)(supports-color@10.2.2)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))(zod@4.4.3)
+ version: 0.18.1(@vue/compiler-sfc@3.5.41)(h3@1.15.11)(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup@4.62.5)(supports-color@10.2.2)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))(zod@4.6.1)
'@nuxtjs/robots':
specifier: ^6.2.0
- version: 6.2.0(3bc4744619a624c5169bc56293f3c985)
+ version: 6.2.0(9963a669bf37ac17e292d4f6f824546f)
'@nuxtjs/sitemap':
specifier: ^8.5.0
- version: 8.5.0(3bc4744619a624c5169bc56293f3c985)
+ version: 8.5.0(9963a669bf37ac17e292d4f6f824546f)
'@octokit/webhooks-methods':
specifier: ^6.0.0
version: 6.0.0
@@ -56,27 +62,30 @@ importers:
'@resvg/resvg-js':
specifier: ^2.6.2
version: 2.6.2
+ '@sqlite.org/sqlite-wasm':
+ specifier: 3.53.0-build1
+ version: 3.53.0-build1
'@vercel/analytics':
specifier: ^2.0.1
- version: 2.0.1(nuxt@4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.5(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
+ version: 2.0.1(nuxt@4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.7(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
'@vercel/functions':
- specifier: ^3.9.5
- version: 3.9.5(ws@8.21.3)
+ specifier: ^3.9.7
+ version: 3.9.7(ws@8.21.3)
'@vercel/global-config':
specifier: ^1.5.1
version: 1.5.1(@opentelemetry/api@1.9.1)
'@vercel/otel':
specifier: ^2.1.3
- version: 2.1.3(@opentelemetry/api-logs@0.221.0)(@opentelemetry/api@1.9.1)(@opentelemetry/instrumentation@0.221.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2))(@opentelemetry/resources@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-logs@0.221.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-metrics@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1))
+ version: 2.1.3(@opentelemetry/api-logs@0.221.0)(@opentelemetry/api@1.9.1)(@opentelemetry/instrumentation@0.221.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2))(@opentelemetry/resources@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-logs@0.221.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-metrics@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1))
'@vercel/speed-insights':
specifier: ^2.0.0
- version: 2.0.0(nuxt@4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.5(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
+ version: 2.0.0(nuxt@4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.7(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
'@vueuse/core':
specifier: ^14.4.0
version: 14.4.0(vue@3.5.41(typescript@6.0.3))
ai:
- specifier: ^7.0.77
- version: 7.0.77(zod@4.4.3)
+ specifier: ^7.0.97
+ version: 7.0.97(zod@4.6.1)
beautiful-mermaid:
specifier: ^1.1.3
version: 1.1.3
@@ -84,8 +93,8 @@ importers:
specifier: ^0.6.2
version: 0.6.2(beautiful-mermaid@1.1.3)(rangi@2.2.0)(shiki@4.4.3)
comark-content:
- specifier: https://pkg.pr.new/comark-content@baefd4d
- version: https://pkg.pr.new/comark-content@baefd4d(@vercel/functions@3.9.5(ws@8.21.3))(beautiful-mermaid@1.1.3)(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))(rangi@2.2.0)(shiki@4.4.3)
+ specifier: ^0.4.0
+ version: 0.4.0(@vercel/functions@3.9.7(ws@8.21.3))(beautiful-mermaid@1.1.3)(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))(rangi@2.2.0)(shiki@4.4.3)
defu:
specifier: ^6.1.7
version: 6.1.7
@@ -93,26 +102,29 @@ importers:
specifier: ^1.1.1
version: 1.1.1
js-yaml:
- specifier: ^5.3.0
- version: 5.3.0
+ specifier: ^5.4.1
+ version: 5.4.1
motion-v:
- specifier: ^2.4.0
+ specifier: ^2.4.2
version: 2.4.2(@vueuse/core@14.4.0(vue@3.5.41(typescript@6.0.3)))(vue@3.5.41(typescript@6.0.3))
nuxt-agent-discovery:
specifier: ^0.5.1
- version: 0.5.1(3adf33016b78b1d9f3d0bf8940d45a28)
+ version: 0.5.1(93fae588a377ff5ba3fd984718cb8b37)
nuxt-llms:
specifier: https://pkg.pr.new/nuxt-content/nuxt-llms/nuxt-llms@f6a9730
version: https://pkg.pr.new/nuxt-content/nuxt-llms/nuxt-llms@f6a9730(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))
nuxt-og-image:
specifier: ^6.7.8
- version: 6.7.8(d69daeae2668f4062a5626b724092832)
+ version: 6.7.8(02a9fd06dc986c7b60d00a5f031b9e09)
nuxt-schema-org:
specifier: ^6.3.1
- version: 6.3.1(922ee4371f2ec5825b5f186a85055e54)
+ version: 6.3.1(374ede1556afd53e77a90e7d78db3c9d)
nuxt-seo-utils:
- specifier: ^8.4.2
- version: 8.4.2(6c2732a7424285fd39f1e74aab3b66f7)
+ specifier: ^8.5.0
+ version: 8.5.0(0e3d028a172b021df1bc33f9e04f4227)
+ nuxt-workers:
+ specifier: ^0.1.0
+ version: 0.1.0(magicast@0.5.4)
pathe:
specifier: ^2.0.3
version: 2.0.3
@@ -130,35 +142,35 @@ importers:
version: 1.6.4
unstorage:
specifier: ^1.17.5
- version: 1.17.5(@vercel/functions@3.9.5(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))
+ version: 1.17.5(@vercel/functions@3.9.7(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))
zod:
- specifier: ^4.4.3
- version: 4.4.3
+ specifier: ^4.6.1
+ version: 4.6.1
devDependencies:
'@nuxt/devtools-kit':
specifier: 4.0.0-alpha.9
version: 4.0.0-alpha.9(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
'@nuxt/eslint-config':
specifier: ^1.17.0
- version: 1.17.0(@typescript-eslint/utils@8.67.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(@vue/compiler-sfc@3.5.41)(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
+ version: 1.17.0(@typescript-eslint/utils@8.67.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(@vue/compiler-sfc@3.5.41)(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
'@opentelemetry/exporter-trace-otlp-proto':
specifier: ^0.221.0
version: 0.221.0(@opentelemetry/api@1.9.1)
'@opentelemetry/resources':
- specifier: ^2.10.0
- version: 2.10.0(@opentelemetry/api@1.9.1)
+ specifier: ^2.11.0
+ version: 2.11.0(@opentelemetry/api@1.9.1)
'@opentelemetry/sdk-trace-base':
- specifier: ^2.10.0
- version: 2.10.0(@opentelemetry/api@1.9.1)
+ specifier: ^2.11.0
+ version: 2.11.0(@opentelemetry/api@1.9.1)
'@opentelemetry/sdk-trace-node':
- specifier: ^2.10.0
- version: 2.10.0(@opentelemetry/api@1.9.1)
+ specifier: ^2.11.0
+ version: 2.11.0(@opentelemetry/api@1.9.1)
eslint:
- specifier: ^10.9.0
- version: 10.9.0(jiti@2.7.0)(supports-color@10.2.2)
+ specifier: ^10.10.0
+ version: 10.10.0(jiti@2.7.0)(supports-color@10.2.2)
nuxt:
specifier: ^4.5.2
- version: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.5(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0)
+ version: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.7(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0)
typescript:
specifier: ^6.0.3
version: 6.0.3
@@ -176,32 +188,32 @@ importers:
version: link:..
nuxt:
specifier: ^4.5.0
- version: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.5(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0)
+ version: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.7(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0)
devDependencies:
nuxtseo-layer-devtools:
specifier: ^5.3.6
- version: 5.3.14(9f33b9b9e7e20aae9f2d2c74340473d1)
+ version: 5.3.14(ca9ba60081d8b5129f3ddf17535a8092)
packages:
- '@ai-sdk/gateway@4.0.62':
- resolution: {integrity: sha512-zR3pustGWhw5eUZHG+fJZx/V/PBe+LxdDpc5hDFWxozG/3MB/+eY62jn+YiR+9uOH+Hx63e5zJoeKLfZfPktWQ==}
+ '@ai-sdk/gateway@4.0.78':
+ resolution: {integrity: sha512-fcamzmFlcy+c/tIc7QcATLe/kArgSVGQ1QxaddMjhOSX8zypc2hmD5sHB2bp1DSFcSxDD32d6usF9a7xLCTULg==}
engines: {node: '>=22'}
peerDependencies:
zod: ^3.25.76 || ^4.1.8
- '@ai-sdk/provider-utils@5.0.29':
- resolution: {integrity: sha512-7EIbwXiXKGa7EFk6tDZpuZBs6lxhEJpOuHeqrDb3Vd85uYdjwkdRuHnZDVDIIb2+QTSRmyph2NrXcbvuO/KAjQ==}
+ '@ai-sdk/provider-utils@5.0.39':
+ resolution: {integrity: sha512-VIFp4Qv3j+V941crFB1y2VG5yeGbeLOFRxiPaZJETkkpo1H5WviLx6H5yRkFkrXIGxnTGygKsPgCAJG0X61Auw==}
engines: {node: '>=22'}
peerDependencies:
zod: ^3.25.76 || ^4.1.8
- '@ai-sdk/provider@4.0.7':
- resolution: {integrity: sha512-6or44XprPzKbr8zkmzosowSE0pxkvJcoojBL+mCZvPUt3kvXp3XSNqeVun9golb1acEfSo6yaEBRT18h2VU+1Q==}
+ '@ai-sdk/provider@4.0.13':
+ resolution: {integrity: sha512-sJvnbFIFLzmKFXIjfwS8XCOAj6puHCawItwvA9PBZgk2f/l/TiC4OSfK3ueDbUVXfRCOn5eJN97EIF10toIOmQ==}
engines: {node: '>=22'}
- '@ai-sdk/vue@4.0.77':
- resolution: {integrity: sha512-Yd6hjkUA+n0QtUgUr+aX0tquKNplp27r3W1vFshx4pYpJnFWuls5TAi4pzmPNZ2ssvM3QNIFhnruSr+q0nPnPg==}
+ '@ai-sdk/vue@4.0.97':
+ resolution: {integrity: sha512-QqtA+WhpI2eSArjqht0a3NplnZrNokKN5BszHxvJuWGfQxkSFiJFRbky9MwvNQDopiafc6nGK0l62PW5AsgOsQ==}
engines: {node: '>=22'}
peerDependencies:
vue: ^3.3.4
@@ -369,6 +381,12 @@ packages:
commander:
optional: true
+ '@cacheable/memory@2.2.0':
+ resolution: {integrity: sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==}
+
+ '@cacheable/utils@2.5.0':
+ resolution: {integrity: sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==}
+
'@capsizecss/unpack@4.0.1':
resolution: {integrity: sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ==}
engines: {node: '>=18'}
@@ -798,8 +816,8 @@ packages:
resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
- '@eslint/plugin-kit@0.7.2':
- resolution: {integrity: sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==}
+ '@eslint/plugin-kit@0.7.3':
+ resolution: {integrity: sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
'@fingerprintjs/botd@2.0.0':
@@ -846,14 +864,20 @@ packages:
'@iconify-json/carbon@1.2.25':
resolution: {integrity: sha512-7GLgXnmi47Skh8DcPPiP8U3vgm3rOVziEe+flCdG/kwiVaeb649U3Eqt/ej4f3NvlZK8BrOxpR3xQlWFLZYblQ==}
- '@iconify-json/lucide@1.2.125':
- resolution: {integrity: sha512-tOCk1QKMtKnCfPAgZRHgjRkQTP7wF5IO+iPKvvp8vxGZYPkSLhx4HTV3Ng0pIZ3wNWrS6kVpHkunJ1dc19L1og==}
+ '@iconify-json/logos@1.2.14':
+ resolution: {integrity: sha512-O36DicXkgAMT6NAsH7MTWlOql8NktHz9fBCItTjz6L/9gyRXw4vQj6qTeVgww5UZWiVugUV1MhVFA/g9HWIkBw==}
+
+ '@iconify-json/lucide@1.2.131':
+ resolution: {integrity: sha512-2h0kGA4utt2OIqTdDHPs7+ft0lK8fRRNx1ItsvUaIlkTKaNc/ADT3F9j2F18Qevm9sWfKlAghOUmxmP32inTPg==}
+
+ '@iconify-json/simple-icons@1.2.95':
+ resolution: {integrity: sha512-QwWgcoiL+eNCD38QM0OStFVFoOgDvzeHrcwMAvATvcFl0VvMEtED92qp3K3juS0H6RZ9BGUlgq9mODPwNWkjJg==}
- '@iconify-json/simple-icons@1.2.93':
- resolution: {integrity: sha512-/XhANjfGYOuqvSR3TmUnkQkINvQ4GVjVuukvymRbxtVFBvIq/yiXJqCDycKcQPT401OYT9H2vIY6ihAlz1QIAw==}
+ '@iconify-json/unjs@1.2.4':
+ resolution: {integrity: sha512-ueSrChjHps8u6jTSDYTAp+4OQ/k+E11PbKZQjKkDVnOxNpC+/fUXXkYgrnqEUgT0rSvUiS0B7X5A8GcsP/PjOA==}
- '@iconify-json/vscode-icons@1.2.74':
- resolution: {integrity: sha512-ZVf1IM5sOvvY+0Gc6jtuD5okkd51mJe/BoVMhWJCu1WqZLSF6XgbSguxUOyuBmMjJXNPSw1OBvPwEVq1bXfiTw==}
+ '@iconify-json/vscode-icons@1.2.77':
+ resolution: {integrity: sha512-yl8e3KlFfLLhiL9CH6YwX6syePeSLLwH2dL4cKGYC27SgdNHL3TRpheAyOjO3fw2+Ovp3tOSF5oj3AhSv4MQ8Q==}
'@iconify/collections@1.0.727':
resolution: {integrity: sha512-n+ipAqz5A6TWExnndwdT6qVX7KfXiw5GNDSxO2otXs6rxFWdHqizHTW1WvmWmT6m2BqQ8lhzUyWSJvm4M3nMAg==}
@@ -873,160 +897,160 @@ packages:
resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==}
engines: {node: '>=18'}
- '@img/sharp-darwin-arm64@0.35.3':
- resolution: {integrity: sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==}
+ '@img/sharp-darwin-arm64@0.35.4':
+ resolution: {integrity: sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==}
engines: {node: '>=20.9.0'}
cpu: [arm64]
os: [darwin]
- '@img/sharp-darwin-x64@0.35.3':
- resolution: {integrity: sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==}
+ '@img/sharp-darwin-x64@0.35.4':
+ resolution: {integrity: sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==}
engines: {node: '>=20.9.0'}
cpu: [x64]
os: [darwin]
- '@img/sharp-freebsd-wasm32@0.35.3':
- resolution: {integrity: sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==}
+ '@img/sharp-freebsd-wasm32@0.35.4':
+ resolution: {integrity: sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==}
engines: {node: '>=20.9.0'}
os: [freebsd]
- '@img/sharp-libvips-darwin-arm64@1.3.2':
- resolution: {integrity: sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==}
+ '@img/sharp-libvips-darwin-arm64@1.3.3':
+ resolution: {integrity: sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==}
cpu: [arm64]
os: [darwin]
- '@img/sharp-libvips-darwin-x64@1.3.2':
- resolution: {integrity: sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==}
+ '@img/sharp-libvips-darwin-x64@1.3.3':
+ resolution: {integrity: sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==}
cpu: [x64]
os: [darwin]
- '@img/sharp-libvips-linux-arm64@1.3.2':
- resolution: {integrity: sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==}
+ '@img/sharp-libvips-linux-arm64@1.3.3':
+ resolution: {integrity: sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==}
cpu: [arm64]
os: [linux]
libc: [glibc]
- '@img/sharp-libvips-linux-arm@1.3.2':
- resolution: {integrity: sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==}
+ '@img/sharp-libvips-linux-arm@1.3.3':
+ resolution: {integrity: sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==}
cpu: [arm]
os: [linux]
libc: [glibc]
- '@img/sharp-libvips-linux-ppc64@1.3.2':
- resolution: {integrity: sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==}
+ '@img/sharp-libvips-linux-ppc64@1.3.3':
+ resolution: {integrity: sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
- '@img/sharp-libvips-linux-riscv64@1.3.2':
- resolution: {integrity: sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==}
+ '@img/sharp-libvips-linux-riscv64@1.3.3':
+ resolution: {integrity: sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
- '@img/sharp-libvips-linux-s390x@1.3.2':
- resolution: {integrity: sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==}
+ '@img/sharp-libvips-linux-s390x@1.3.3':
+ resolution: {integrity: sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==}
cpu: [s390x]
os: [linux]
libc: [glibc]
- '@img/sharp-libvips-linux-x64@1.3.2':
- resolution: {integrity: sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==}
+ '@img/sharp-libvips-linux-x64@1.3.3':
+ resolution: {integrity: sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==}
cpu: [x64]
os: [linux]
libc: [glibc]
- '@img/sharp-libvips-linuxmusl-arm64@1.3.2':
- resolution: {integrity: sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==}
+ '@img/sharp-libvips-linuxmusl-arm64@1.3.3':
+ resolution: {integrity: sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==}
cpu: [arm64]
os: [linux]
libc: [musl]
- '@img/sharp-libvips-linuxmusl-x64@1.3.2':
- resolution: {integrity: sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==}
+ '@img/sharp-libvips-linuxmusl-x64@1.3.3':
+ resolution: {integrity: sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==}
cpu: [x64]
os: [linux]
libc: [musl]
- '@img/sharp-linux-arm64@0.35.3':
- resolution: {integrity: sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==}
+ '@img/sharp-linux-arm64@0.35.4':
+ resolution: {integrity: sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==}
engines: {node: '>=20.9.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
- '@img/sharp-linux-arm@0.35.3':
- resolution: {integrity: sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==}
+ '@img/sharp-linux-arm@0.35.4':
+ resolution: {integrity: sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==}
engines: {node: '>=20.9.0'}
cpu: [arm]
os: [linux]
libc: [glibc]
- '@img/sharp-linux-ppc64@0.35.3':
- resolution: {integrity: sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==}
+ '@img/sharp-linux-ppc64@0.35.4':
+ resolution: {integrity: sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==}
engines: {node: '>=20.9.0'}
cpu: [ppc64]
os: [linux]
libc: [glibc]
- '@img/sharp-linux-riscv64@0.35.3':
- resolution: {integrity: sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==}
+ '@img/sharp-linux-riscv64@0.35.4':
+ resolution: {integrity: sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==}
engines: {node: '>=20.9.0'}
cpu: [riscv64]
os: [linux]
libc: [glibc]
- '@img/sharp-linux-s390x@0.35.3':
- resolution: {integrity: sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==}
+ '@img/sharp-linux-s390x@0.35.4':
+ resolution: {integrity: sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==}
engines: {node: '>=20.9.0'}
cpu: [s390x]
os: [linux]
libc: [glibc]
- '@img/sharp-linux-x64@0.35.3':
- resolution: {integrity: sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==}
+ '@img/sharp-linux-x64@0.35.4':
+ resolution: {integrity: sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==}
engines: {node: '>=20.9.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
- '@img/sharp-linuxmusl-arm64@0.35.3':
- resolution: {integrity: sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==}
+ '@img/sharp-linuxmusl-arm64@0.35.4':
+ resolution: {integrity: sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==}
engines: {node: '>=20.9.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
- '@img/sharp-linuxmusl-x64@0.35.3':
- resolution: {integrity: sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==}
+ '@img/sharp-linuxmusl-x64@0.35.4':
+ resolution: {integrity: sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==}
engines: {node: '>=20.9.0'}
cpu: [x64]
os: [linux]
libc: [musl]
- '@img/sharp-wasm32@0.35.3':
- resolution: {integrity: sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==}
+ '@img/sharp-wasm32@0.35.4':
+ resolution: {integrity: sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==}
engines: {node: '>=20.9.0'}
- '@img/sharp-webcontainers-wasm32@0.35.3':
- resolution: {integrity: sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==}
+ '@img/sharp-webcontainers-wasm32@0.35.4':
+ resolution: {integrity: sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==}
engines: {node: '>=20.9.0'}
cpu: [wasm32]
- '@img/sharp-win32-arm64@0.35.3':
- resolution: {integrity: sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==}
+ '@img/sharp-win32-arm64@0.35.4':
+ resolution: {integrity: sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==}
engines: {node: '>=20.9.0'}
cpu: [arm64]
os: [win32]
- '@img/sharp-win32-ia32@0.35.3':
- resolution: {integrity: sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==}
+ '@img/sharp-win32-ia32@0.35.4':
+ resolution: {integrity: sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==}
engines: {node: ^20.9.0}
cpu: [ia32]
os: [win32]
- '@img/sharp-win32-x64@0.35.3':
- resolution: {integrity: sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==}
+ '@img/sharp-win32-x64@0.35.4':
+ resolution: {integrity: sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==}
engines: {node: '>=20.9.0'}
cpu: [x64]
os: [win32]
@@ -1067,6 +1091,15 @@ packages:
'@jridgewell/trace-mapping@0.3.31':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+ '@keyv/bigmap@1.3.1':
+ resolution: {integrity: sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==}
+ engines: {node: '>= 18'}
+ peerDependencies:
+ keyv: ^5.6.0
+
+ '@keyv/serialize@1.1.1':
+ resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==}
+
'@kwsites/file-exists@1.1.1':
resolution: {integrity: sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==}
@@ -1179,6 +1212,10 @@ packages:
'@nuxt/icon@2.5.1':
resolution: {integrity: sha512-zBP72Po7BS+tXzoeDRA/Y9TTY77OIcNCyzfgXsOmep7zZShTEoe4p1WZBXce/9oJDK3YXmbYC3ILQ7XvqM4/XA==}
+ '@nuxt/kit@3.21.11':
+ resolution: {integrity: sha512-0Xi3tgwN77w43Q8GCPIrvWmF1J7Peehkts44E0uKNIml9lB8WoUn8YxyUjxBv47XtVR86NWoWALAT+/IEMHJEA==}
+ engines: {node: '>=18.12.0'}
+
'@nuxt/kit@4.5.2':
resolution: {integrity: sha512-l66LU9DcJYjmNwqwAj2I5UGRrUbnG2DOKGChnN70zIGtn0eq/z87gi/FRgha6eMb9/FmB1PFHgtx6PWVml1C2Q==}
engines: {node: '>=18.12.0'}
@@ -1210,6 +1247,65 @@ packages:
peerDependencies:
'@nuxt/kit': '>=3.0.0'
+ '@nuxt/ui@4.11.0':
+ resolution: {integrity: sha512-gDs67/fWk3kipcEV4Pc5h1VnZD1glfWfINmJU7s3qpkxxRTkfkPxnUszXG664whNWcoqucS1WgZ/rNjZa3pTMw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ '@inertiajs/vue3': ^2.0.7 || ^3.0.0
+ '@internationalized/date': ^3.0.0
+ '@internationalized/number': ^3.0.0
+ '@nuxt/content': ^3.0.0
+ '@tiptap/core': ^3
+ '@tiptap/extension-bubble-menu': ^3
+ '@tiptap/extension-code': ^3
+ '@tiptap/extension-collaboration': ^3
+ '@tiptap/extension-drag-handle': ^3
+ '@tiptap/extension-drag-handle-vue-3': ^3
+ '@tiptap/extension-floating-menu': ^3
+ '@tiptap/extension-horizontal-rule': ^3
+ '@tiptap/extension-image': ^3
+ '@tiptap/extension-mention': ^3
+ '@tiptap/extension-node-range': ^3
+ '@tiptap/extension-placeholder': ^3
+ '@tiptap/markdown': ^3
+ '@tiptap/pm': ^3
+ '@tiptap/starter-kit': ^3
+ '@tiptap/suggestion': ^3
+ '@tiptap/vue-3': ^3
+ ai: ^6 || ^7
+ joi: ^18.0.0
+ superstruct: ^2.0.0
+ tailwindcss: ^4.0.0
+ typescript: ^5.6.3 || ^6.0.0 || ^7.0.0
+ valibot: ^1.0.0
+ vue-router: ^4.5.0 || ^5.0.0
+ yup: ^1.7.0
+ zod: ^3.24.0 || ^4.0.0
+ peerDependenciesMeta:
+ '@inertiajs/vue3':
+ optional: true
+ '@internationalized/date':
+ optional: true
+ '@internationalized/number':
+ optional: true
+ '@nuxt/content':
+ optional: true
+ ai:
+ optional: true
+ joi:
+ optional: true
+ superstruct:
+ optional: true
+ valibot:
+ optional: true
+ vue-router:
+ optional: true
+ yup:
+ optional: true
+ zod:
+ optional: true
+
'@nuxt/ui@4.11.1':
resolution: {integrity: sha512-6/xTMQNO4bcVesaaiIorH65cZ3eu0xenH+gC2L/b9pfmeVc0aMGXDKpW2JxoVUlrlcHo/beoUTI0B164HcwweQ==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -1343,8 +1439,8 @@ packages:
resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==}
engines: {node: '>=8.0.0'}
- '@opentelemetry/context-async-hooks@2.10.0':
- resolution: {integrity: sha512-bvyMcgLEkozzSzpEEEo1OMoeQ97bxj6Qs2uN3mPrSdDvObMI1myffD/BPqcLlzZO9//d1SqQA/WPw7Cz2AiqhA==}
+ '@opentelemetry/context-async-hooks@2.11.0':
+ resolution: {integrity: sha512-Tr79DyWI8itsBdg+jH+opjfrwLzX+erk1/ExkIwhWoAVjVrJIn2y5+cGjTC0Vy8fyNIA/y8wuJPZwr1T3xCZeQ==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.0.0 <1.10.0'
@@ -1355,6 +1451,12 @@ packages:
peerDependencies:
'@opentelemetry/api': '>=1.0.0 <1.10.0'
+ '@opentelemetry/core@2.11.0':
+ resolution: {integrity: sha512-7YP44XH0tV6+Mb54x2YGf84i7yi+31MBZlE8JwvozkxyTvXbSp10X7cI7YE49ChJ3shMJoBmCJF3+1QFBJctGA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.0.0 <1.10.0'
+
'@opentelemetry/exporter-trace-otlp-proto@0.221.0':
resolution: {integrity: sha512-Z9i2T7vgZbWe9rSLYxXVIbeW+XyzUq4rZanW3ZyVNwVDqCsh0EJKUgBWWQ0CZfeuUA+RQPzKgJQHMuWAUnKqXw==}
engines: {node: ^18.19.0 || >=20.6.0}
@@ -1385,6 +1487,12 @@ packages:
peerDependencies:
'@opentelemetry/api': '>=1.3.0 <1.10.0'
+ '@opentelemetry/resources@2.11.0':
+ resolution: {integrity: sha512-Ie7+8q8MDF4FAEQCKVMTx3ReUvxiIAgIiiW3c9JdmP8+HMcDy20puT+AHjexnExgnbvBxjQ9fjkFDWrikJ2jQA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.3.0 <1.10.0'
+
'@opentelemetry/sdk-logs@0.221.0':
resolution: {integrity: sha512-FaDcazjyMp7TZZZAsqbo4IkovP0UegoCu0EBkiNt+qCqvUf7FPAsfcrZ3+ZEkKgXZ/jHafop+JoGPDk3A0SmLg==}
engines: {node: ^18.19.0 || >=20.6.0}
@@ -1397,14 +1505,14 @@ packages:
peerDependencies:
'@opentelemetry/api': '>=1.9.0 <1.10.0'
- '@opentelemetry/sdk-trace-base@2.10.0':
- resolution: {integrity: sha512-GuYQQT7QD2EeO8lcZLRQzcbOyhqAzL+6WWTKTU9mSUBYBazkEDl+VrQcXQhbB08OWM9anD1aHleVadzulpOaUQ==}
+ '@opentelemetry/sdk-trace-base@2.11.0':
+ resolution: {integrity: sha512-H19x/TX/LZdqiYOjM7fqtSxwlplC5pgelavqbQdHbhdq0q/AI/TGkM2dfGuuynTXmJPeF2HoZVoPDu+TGoW78A==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.3.0 <1.10.0'
- '@opentelemetry/sdk-trace-node@2.10.0':
- resolution: {integrity: sha512-GZK/G6oZyBLGlH1pUgeDch7D91KoHd2uotUGIkWCPi9GI5T9X0p4L7nNAMDR1BQjkRYoDqo+ddfVx9t5Uhys+Q==}
+ '@opentelemetry/sdk-trace-node@2.11.0':
+ resolution: {integrity: sha512-CuvCMJmZxswhNLlM2LfuLOW3h3fZujA4hsG4B+Sz4dX2zvaXO8Ng74cnDHWD64gLszTlhiG3c0iNUjj4g+0/sA==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.0.0 <1.10.0'
@@ -1415,6 +1523,12 @@ packages:
peerDependencies:
'@opentelemetry/api': '>=1.3.0 <1.10.0'
+ '@opentelemetry/sdk-trace@2.11.0':
+ resolution: {integrity: sha512-fFnTqGm8/G73GQVnxYi7LXa1ZVYEUvgL6XI1LpvV0bPC7WQ/ZGgKxCSl8FnlZBKto9JHHEFTO6s6CUpvvtwFrA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.3.0 <1.10.0'
+
'@opentelemetry/semantic-conventions@1.43.0':
resolution: {integrity: sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==}
engines: {node: '>=14'}
@@ -2015,6 +2129,10 @@ packages:
'@speed-highlight/core@1.2.24':
resolution: {integrity: sha512-qeW2e1l78afw8VhRPfPQ1Gjj+KU5XFQ/OFV5ti6eTa9bruO7mJyZtA4vw0ofqmA3tKCkROE9xLk3VZoeRc98nw==}
+ '@sqlite.org/sqlite-wasm@3.53.0-build1':
+ resolution: {integrity: sha512-PfWPWN2n+/37doa8oh2/oUXk4OOsRYZsxc1W1sDXIGb/Pu5Yrb+f2eyYpgQMGITVX7HVgxhs9P18Rc6I97ym/g==}
+ engines: {node: '>=22'}
+
'@standard-schema/spec@1.1.0':
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
@@ -2662,15 +2780,15 @@ packages:
vue-router:
optional: true
- '@vercel/cli-config@0.2.4':
- resolution: {integrity: sha512-kZ5SojbrV06GHoU6QIWGwDXLov+s9rWZ7QqdqKfJfBGCNUieGfgaCjeeenNy8Y+QC0bwC0dZ2B4l5Hvdmrgpdw==}
+ '@vercel/cli-config@0.2.6':
+ resolution: {integrity: sha512-2AsKCf6gE/Eniq09ARm1RK9rQMV5pcAHU7BFcR9MISO+4/RsjjkaYbQN6G85/xi9pYZ5CJNXvYn4TI6p1jKBcg==}
'@vercel/cli-exec@1.0.1':
resolution: {integrity: sha512-g9XerViJ/paZujufXYcu5XYI2vU2rtB4sgdpjUHde5RnOkdmpu0ngH46LCFGHoPXO/C+qDPSczIHIRN+8Q2YKQ==}
engines: {node: '>= 18'}
- '@vercel/functions@3.9.5':
- resolution: {integrity: sha512-EUfqlb7AzoEh7URlMNAO4jbJiLWz9grDBHvfjKTDvEP9c8y3DqX3SWPvfaQkUjtkm3b83flhaUUMuewdHa+qmw==}
+ '@vercel/functions@3.9.7':
+ resolution: {integrity: sha512-ICLzHbVZs10igkVMW/ovEMIXFa5ctpEjTa+VWRCOQbIqg9ALZcQcRjcfDuQZ/g2XtPS08EvHjzm538YJU9WsXA==}
engines: {node: '>= 20'}
peerDependencies:
'@aws-sdk/credential-provider-web-identity': '*'
@@ -2705,8 +2823,8 @@ packages:
resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==}
engines: {node: '>= 20'}
- '@vercel/oidc@3.8.5':
- resolution: {integrity: sha512-RwXYtnt6za+5UO4IaLywN/6B95AlLqynPRUWRJxeJ/qufwkcLUbZNUxYtzT0uMpuraWhlNcGqPNGkTnZr4BGBw==}
+ '@vercel/oidc@3.8.7':
+ resolution: {integrity: sha512-fRu59npOu+1vsV570tiLKskfuRyOJ1MqceAkXbTIfWPnM+c9ve1tSK81oj4umKKirymlGUss3V60Lm5I38rKDw==}
engines: {node: '>= 20'}
'@vercel/otel@2.1.3':
@@ -2977,8 +3095,8 @@ packages:
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
engines: {node: '>= 14'}
- ai@7.0.77:
- resolution: {integrity: sha512-muLtBSTAUCreR77L16w4AFBiX2gK/RNt84EKp8m03SN9+MfNlC5EGqYYttRjYKV3xe0a33yj1Zawj1EnjejIWw==}
+ ai@7.0.97:
+ resolution: {integrity: sha512-tQGZ234p/bk5X/LNQdB43a5/z1Bve7wK0EOR1AwN6xrosZih4MIC/3pXhcnytcpBJHMbhLISW6J1dFTd/tnwPA==}
engines: {node: '>=22'}
peerDependencies:
zod: ^3.25.76 || ^4.1.8
@@ -3204,6 +3322,9 @@ packages:
resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==}
engines: {node: '>=20.19.0'}
+ cacheable@2.5.0:
+ resolution: {integrity: sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==}
+
call-bind-apply-helpers@1.0.2:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
@@ -3281,9 +3402,8 @@ packages:
colortranslator@5.0.0:
resolution: {integrity: sha512-Z3UPUKasUVDFCDYAjP2fmlVRf1jFHJv1izAmPjiOa0OCIw1W7iC8PZ2GsoDa8uZv+mKyWopxxStT9q05+27h7w==}
- comark-content@https://pkg.pr.new/comark-content@baefd4d:
- resolution: {integrity: sha512-7E/3OIIKPBI5XJ3s/aEtptAP49Of9W/EQVjzsbGnAZgCxvtlpqeSq1l+8GUbMDYqNy20rLWXlk7JdXTd8rsj/g==, tarball: https://pkg.pr.new/comark-content@baefd4d}
- version: 0.3.0
+ comark-content@0.4.0:
+ resolution: {integrity: sha512-wUCFpDdEl9tNXcAFVMkqpQL+Aa5hcg1YL5X8r+5oAohQkofRp+We4puz6QP4XR33MPbasA41pPfIVf4j+WrT4Q==}
hasBin: true
comark@0.6.2:
@@ -3842,8 +3962,8 @@ packages:
resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
- eslint@10.9.0:
- resolution: {integrity: sha512-5KeEOJZBfEVA47boFiBsf+6MmmJpffM7qEBg4pLla2e4nlKgdKlqCW0oSLOGsT8Wl5uCGJptLV1bkaiShj90Gw==}
+ eslint@10.10.0:
+ resolution: {integrity: sha512-NPXn6r5zl4uET1DAVPaOwzX3rut4c0wcmw3dWJAfOsTM5+TogXo0DDjz8pwm/hL8cyVNpHqeK4JpN0NjnyFFNw==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
hasBin: true
peerDependencies:
@@ -3984,9 +4104,8 @@ packages:
fflate@0.7.5:
resolution: {integrity: sha512-QieYf//cis6ywHNi5qW1+PXPQ4bC+XVJAtS4AXIML8P76GroEiOxm/oQtn1f02UkJY1+KsXMJcC+R2v/Eg4G3g==}
- file-entry-cache@8.0.0:
- resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
- engines: {node: '>=16.0.0'}
+ file-entry-cache@11.1.5:
+ resolution: {integrity: sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==}
file-uri-to-path@1.0.0:
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
@@ -4007,9 +4126,8 @@ packages:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
engines: {node: '>=10'}
- flat-cache@4.0.1:
- resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
- engines: {node: '>=16'}
+ flat-cache@6.1.23:
+ resolution: {integrity: sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==}
flatted@3.4.4:
resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==}
@@ -4167,6 +4285,10 @@ packages:
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
engines: {node: '>= 0.4'}
+ hashery@1.5.1:
+ resolution: {integrity: sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==}
+ engines: {node: '>=20'}
+
hasown@2.0.4:
resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==}
engines: {node: '>= 0.4'}
@@ -4194,6 +4316,12 @@ packages:
hookable@6.1.1:
resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==}
+ hookified@1.15.1:
+ resolution: {integrity: sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==}
+
+ hookified@2.2.0:
+ resolution: {integrity: sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==}
+
html-entities@2.6.0:
resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==}
@@ -4404,8 +4532,8 @@ packages:
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
- js-yaml@5.3.0:
- resolution: {integrity: sha512-muutsYr+e2+d3rTgUGslq5rxbBlUy3cJ61IsHag2QNDQV+7zXWjkUpmALIajhrlLlrgRUiymj6U3zUr/TMK84Q==}
+ js-yaml@5.4.1:
+ resolution: {integrity: sha512-28R/k+NAjeuf7+CKlTxWZVExJGwVVLwY06DgEnOMz2gEpfNkDcD7QvyiVPT0xy0XXhU8vHsd4Ot42OOPdJG7dQ==}
hasBin: true
jsdoc-type-pratt-parser@8.0.0:
@@ -4421,9 +4549,6 @@ packages:
engines: {node: '>=6'}
hasBin: true
- json-buffer@3.0.1:
- resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
-
json-schema-traverse@0.4.1:
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
@@ -4444,8 +4569,8 @@ packages:
engines: {node: '>=6'}
hasBin: true
- keyv@4.5.4:
- resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+ keyv@5.6.0:
+ resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==}
kleur@4.1.5:
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
@@ -5004,8 +5129,8 @@ packages:
zod:
optional: true
- nuxt-seo-utils@8.4.2:
- resolution: {integrity: sha512-l+UULXKMMuL5MC779k/lH1Rtv2nNbh5qWCBMEQp61LXo1HrMFfXoP6BdOAUj+dcAXhEdOAVZIXWH2qiBM3oAjg==}
+ nuxt-seo-utils@8.5.0:
+ resolution: {integrity: sha512-vq5rP7xBLEYGUBskzllqg0tmRTt8EyhzX1tuvWEGRt4JKAhw2HN75GUP1RpAM4n6QYqHyh6U8YkJZjEuot0wpQ==}
hasBin: true
peerDependencies:
'@unhead/vue': ^2.0.7 || ^3.0.0
@@ -5034,6 +5159,9 @@ packages:
peerDependencies:
vue: ^3.5.30
+ nuxt-workers@0.1.0:
+ resolution: {integrity: sha512-npsxy72FRQZkxHV1Y+KCkuSvb2Y/7Tcp7xGXPHXXVQ5/oIZ5+69VAudfdhpuwPN1JZJn9ULr01vKRLamENsTew==}
+
nuxt@4.5.2:
resolution: {integrity: sha512-tR3fcqeHlHmmkLMpIg3V7Y+1ltr302lW8djMw/iy+myfo7QSSz+BVJDuQhg5j73b9oteSyBfOKTDYTgvMtj6TA==}
engines: {node: ^22.19.0 || ^24.11.0 || >=26.0.0}
@@ -5510,6 +5638,10 @@ packages:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
+ qified@0.10.1:
+ resolution: {integrity: sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==}
+ engines: {node: '>=20'}
+
qs@6.15.3:
resolution: {integrity: sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==}
engines: {node: '>=0.6'}
@@ -5589,6 +5721,11 @@ packages:
resolution: {integrity: sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==}
hasBin: true
+ reka-ui@2.10.3:
+ resolution: {integrity: sha512-nJGZbwcha8AcP2wbnjodfzsciTKBqp1mIzepC9Pi2xDI/YK3++ej3vpJOSPyxqrkq1Oorj4K+BdJkbVCV6x6ag==}
+ peerDependencies:
+ vue: '>= 3.4.0'
+
reka-ui@2.10.4:
resolution: {integrity: sha512-kbS5GAbkHkYj0EVKAg5ZPEPndhBjeUFSa1Aq5m5ftQwyHnB1v5tw8X6fcwfKH/ry3StPXblMWM1DW82MVS71cw==}
peerDependencies:
@@ -5730,8 +5867,8 @@ packages:
setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
- sharp@0.35.3:
- resolution: {integrity: sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==}
+ sharp@0.35.4:
+ resolution: {integrity: sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==}
engines: {node: '>=20.9.0'}
peerDependencies:
'@types/node': '*'
@@ -6718,38 +6855,38 @@ packages:
zod@4.1.11:
resolution: {integrity: sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg==}
- zod@4.4.3:
- resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==}
+ zod@4.6.1:
+ resolution: {integrity: sha512-341aRWQsve0rvronKNTqZpjmzdbUDlFuzHaI/XLg/Ej82qffDJRRfBTCuv7+9q/rMjB6LSLyEBnW4InJeMtt/Q==}
zwitch@2.0.4:
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
snapshots:
- '@ai-sdk/gateway@4.0.62(zod@4.4.3)':
+ '@ai-sdk/gateway@4.0.78(zod@4.6.1)':
dependencies:
- '@ai-sdk/provider': 4.0.7
- '@ai-sdk/provider-utils': 5.0.29(zod@4.4.3)
+ '@ai-sdk/provider': 4.0.13
+ '@ai-sdk/provider-utils': 5.0.39(zod@4.6.1)
'@vercel/oidc': 3.2.0
- zod: 4.4.3
+ zod: 4.6.1
- '@ai-sdk/provider-utils@5.0.29(zod@4.4.3)':
+ '@ai-sdk/provider-utils@5.0.39(zod@4.6.1)':
dependencies:
- '@ai-sdk/provider': 4.0.7
+ '@ai-sdk/provider': 4.0.13
'@standard-schema/spec': 1.1.0
'@workflow/serde': 4.1.0
eventsource-parser: 3.1.1
undici: 7.29.0
- zod: 4.4.3
+ zod: 4.6.1
- '@ai-sdk/provider@4.0.7':
+ '@ai-sdk/provider@4.0.13':
dependencies:
json-schema: 0.4.0
- '@ai-sdk/vue@4.0.77(vue@3.5.41(typescript@6.0.3))(zod@4.4.3)':
+ '@ai-sdk/vue@4.0.97(vue@3.5.41(typescript@6.0.3))(zod@4.6.1)':
dependencies:
- '@ai-sdk/provider-utils': 5.0.29(zod@4.4.3)
- ai: 7.0.77(zod@4.4.3)
+ '@ai-sdk/provider-utils': 5.0.39(zod@4.6.1)
+ ai: 7.0.97(zod@4.6.1)
swrv: 1.2.0(vue@3.5.41(typescript@6.0.3))
vue: 3.5.41(typescript@6.0.3)
transitivePeerDependencies:
@@ -6960,6 +7097,18 @@ snapshots:
optionalDependencies:
citty: 0.2.2
+ '@cacheable/memory@2.2.0':
+ dependencies:
+ '@cacheable/utils': 2.5.0
+ '@keyv/bigmap': 1.3.1(keyv@5.6.0)
+ hookified: 1.15.1
+ keyv: 5.6.0
+
+ '@cacheable/utils@2.5.0':
+ dependencies:
+ hashery: 1.5.1
+ keyv: 5.6.0
+
'@capsizecss/unpack@4.0.1':
dependencies:
fontkitten: 1.0.3
@@ -6980,12 +7129,12 @@ snapshots:
'@colordx/core@5.6.0': {}
- '@comark/nuxt@0.6.2(a7af4bed794ccaa8b491cb4661979ebb)':
+ '@comark/nuxt@0.6.2(d45826d7a399b3ded44dd0ff86d4ffd7)':
dependencies:
'@comark/vue': 0.6.2(beautiful-mermaid@1.1.3)(rangi@2.2.0)(shiki@4.4.3)(vue@3.5.41(typescript@6.0.3))
'@nuxt/kit': 4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))
comark: 0.6.2(beautiful-mermaid@1.1.3)(rangi@2.2.0)(shiki@4.4.3)
- nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.5(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0)
+ nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.7(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0)
transitivePeerDependencies:
- beautiful-mermaid
- katex
@@ -7221,18 +7370,18 @@ snapshots:
'@esbuild/win32-x64@0.28.2':
optional: true
- '@eslint-community/eslint-utils@4.10.1(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))':
+ '@eslint-community/eslint-utils@4.10.1(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))':
dependencies:
- eslint: 10.9.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.10.0(jiti@2.7.0)(supports-color@10.2.2)
eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.12.2': {}
- '@eslint/compat@2.1.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))':
+ '@eslint/compat@2.1.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))':
dependencies:
'@eslint/core': 1.2.1
optionalDependencies:
- eslint: 10.9.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.10.0(jiti@2.7.0)(supports-color@10.2.2)
'@eslint/config-array@0.23.5(supports-color@10.2.2)':
dependencies:
@@ -7259,13 +7408,13 @@ snapshots:
mdn-data: 2.29.0
source-map-js: 1.2.1
- '@eslint/js@10.0.1(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))':
+ '@eslint/js@10.0.1(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))':
optionalDependencies:
- eslint: 10.9.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.10.0(jiti@2.7.0)(supports-color@10.2.2)
'@eslint/object-schema@3.0.5': {}
- '@eslint/plugin-kit@0.7.2':
+ '@eslint/plugin-kit@0.7.3':
dependencies:
'@eslint/core': 1.2.1
levn: 0.4.1
@@ -7316,15 +7465,23 @@ snapshots:
dependencies:
'@iconify/types': 2.0.0
- '@iconify-json/lucide@1.2.125':
+ '@iconify-json/logos@1.2.14':
+ dependencies:
+ '@iconify/types': 2.0.0
+
+ '@iconify-json/lucide@1.2.131':
dependencies:
'@iconify/types': 2.0.0
- '@iconify-json/simple-icons@1.2.93':
+ '@iconify-json/simple-icons@1.2.95':
dependencies:
'@iconify/types': 2.0.0
- '@iconify-json/vscode-icons@1.2.74':
+ '@iconify-json/unjs@1.2.4':
+ dependencies:
+ '@iconify/types': 2.0.0
+
+ '@iconify-json/vscode-icons@1.2.77':
dependencies:
'@iconify/types': 2.0.0
@@ -7348,108 +7505,108 @@ snapshots:
'@img/colour@1.1.0':
optional: true
- '@img/sharp-darwin-arm64@0.35.3':
+ '@img/sharp-darwin-arm64@0.35.4':
optionalDependencies:
- '@img/sharp-libvips-darwin-arm64': 1.3.2
+ '@img/sharp-libvips-darwin-arm64': 1.3.3
optional: true
- '@img/sharp-darwin-x64@0.35.3':
+ '@img/sharp-darwin-x64@0.35.4':
optionalDependencies:
- '@img/sharp-libvips-darwin-x64': 1.3.2
+ '@img/sharp-libvips-darwin-x64': 1.3.3
optional: true
- '@img/sharp-freebsd-wasm32@0.35.3':
+ '@img/sharp-freebsd-wasm32@0.35.4':
dependencies:
- '@img/sharp-wasm32': 0.35.3
+ '@img/sharp-wasm32': 0.35.4
optional: true
- '@img/sharp-libvips-darwin-arm64@1.3.2':
+ '@img/sharp-libvips-darwin-arm64@1.3.3':
optional: true
- '@img/sharp-libvips-darwin-x64@1.3.2':
+ '@img/sharp-libvips-darwin-x64@1.3.3':
optional: true
- '@img/sharp-libvips-linux-arm64@1.3.2':
+ '@img/sharp-libvips-linux-arm64@1.3.3':
optional: true
- '@img/sharp-libvips-linux-arm@1.3.2':
+ '@img/sharp-libvips-linux-arm@1.3.3':
optional: true
- '@img/sharp-libvips-linux-ppc64@1.3.2':
+ '@img/sharp-libvips-linux-ppc64@1.3.3':
optional: true
- '@img/sharp-libvips-linux-riscv64@1.3.2':
+ '@img/sharp-libvips-linux-riscv64@1.3.3':
optional: true
- '@img/sharp-libvips-linux-s390x@1.3.2':
+ '@img/sharp-libvips-linux-s390x@1.3.3':
optional: true
- '@img/sharp-libvips-linux-x64@1.3.2':
+ '@img/sharp-libvips-linux-x64@1.3.3':
optional: true
- '@img/sharp-libvips-linuxmusl-arm64@1.3.2':
+ '@img/sharp-libvips-linuxmusl-arm64@1.3.3':
optional: true
- '@img/sharp-libvips-linuxmusl-x64@1.3.2':
+ '@img/sharp-libvips-linuxmusl-x64@1.3.3':
optional: true
- '@img/sharp-linux-arm64@0.35.3':
+ '@img/sharp-linux-arm64@0.35.4':
optionalDependencies:
- '@img/sharp-libvips-linux-arm64': 1.3.2
+ '@img/sharp-libvips-linux-arm64': 1.3.3
optional: true
- '@img/sharp-linux-arm@0.35.3':
+ '@img/sharp-linux-arm@0.35.4':
optionalDependencies:
- '@img/sharp-libvips-linux-arm': 1.3.2
+ '@img/sharp-libvips-linux-arm': 1.3.3
optional: true
- '@img/sharp-linux-ppc64@0.35.3':
+ '@img/sharp-linux-ppc64@0.35.4':
optionalDependencies:
- '@img/sharp-libvips-linux-ppc64': 1.3.2
+ '@img/sharp-libvips-linux-ppc64': 1.3.3
optional: true
- '@img/sharp-linux-riscv64@0.35.3':
+ '@img/sharp-linux-riscv64@0.35.4':
optionalDependencies:
- '@img/sharp-libvips-linux-riscv64': 1.3.2
+ '@img/sharp-libvips-linux-riscv64': 1.3.3
optional: true
- '@img/sharp-linux-s390x@0.35.3':
+ '@img/sharp-linux-s390x@0.35.4':
optionalDependencies:
- '@img/sharp-libvips-linux-s390x': 1.3.2
+ '@img/sharp-libvips-linux-s390x': 1.3.3
optional: true
- '@img/sharp-linux-x64@0.35.3':
+ '@img/sharp-linux-x64@0.35.4':
optionalDependencies:
- '@img/sharp-libvips-linux-x64': 1.3.2
+ '@img/sharp-libvips-linux-x64': 1.3.3
optional: true
- '@img/sharp-linuxmusl-arm64@0.35.3':
+ '@img/sharp-linuxmusl-arm64@0.35.4':
optionalDependencies:
- '@img/sharp-libvips-linuxmusl-arm64': 1.3.2
+ '@img/sharp-libvips-linuxmusl-arm64': 1.3.3
optional: true
- '@img/sharp-linuxmusl-x64@0.35.3':
+ '@img/sharp-linuxmusl-x64@0.35.4':
optionalDependencies:
- '@img/sharp-libvips-linuxmusl-x64': 1.3.2
+ '@img/sharp-libvips-linuxmusl-x64': 1.3.3
optional: true
- '@img/sharp-wasm32@0.35.3':
+ '@img/sharp-wasm32@0.35.4':
dependencies:
'@emnapi/runtime': 1.11.3
optional: true
- '@img/sharp-webcontainers-wasm32@0.35.3':
+ '@img/sharp-webcontainers-wasm32@0.35.4':
dependencies:
- '@img/sharp-wasm32': 0.35.3
+ '@img/sharp-wasm32': 0.35.4
optional: true
- '@img/sharp-win32-arm64@0.35.3':
+ '@img/sharp-win32-arm64@0.35.4':
optional: true
- '@img/sharp-win32-ia32@0.35.3':
+ '@img/sharp-win32-ia32@0.35.4':
optional: true
- '@img/sharp-win32-x64@0.35.3':
+ '@img/sharp-win32-x64@0.35.4':
optional: true
'@internationalized/date@3.12.3':
@@ -7499,6 +7656,14 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
+ '@keyv/bigmap@1.3.1(keyv@5.6.0)':
+ dependencies:
+ hashery: 1.5.1
+ hookified: 1.15.1
+ keyv: 5.6.0
+
+ '@keyv/serialize@1.1.1': {}
+
'@kwsites/file-exists@1.1.1(supports-color@10.2.2)':
dependencies:
debug: 4.4.3(supports-color@10.2.2)
@@ -7520,7 +7685,7 @@ snapshots:
- encoding
- supports-color
- '@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.4.3)':
+ '@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.6.1)':
dependencies:
'@hono/node-server': 2.1.1(hono@4.13.4)
ajv: 8.20.0
@@ -7537,8 +7702,8 @@ snapshots:
json-schema-typed: 8.0.2
pkce-challenge: 5.0.1
raw-body: 3.0.2
- zod: 4.4.3
- zod-to-json-schema: 3.25.2(zod@4.4.3)
+ zod: 4.6.1
+ zod-to-json-schema: 3.25.2(zod@4.6.1)
transitivePeerDependencies:
- supports-color
@@ -7655,7 +7820,7 @@ snapshots:
pkg-types: 2.3.1
semver: 7.8.5
- '@nuxt/devtools@3.4.2(@vercel/functions@3.9.5(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))(magic-string@1.2.3)(oxc-parser@0.143.0)(rolldown@1.2.5)(supports-color@10.2.2)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))':
+ '@nuxt/devtools@3.4.2(@vercel/functions@3.9.7(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))(magic-string@1.2.3)(oxc-parser@0.143.0)(rolldown@1.2.5)(supports-color@10.2.2)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))':
dependencies:
'@nuxt/devtools-kit': 3.4.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
'@nuxt/devtools-wizard': 3.4.2
@@ -7685,7 +7850,7 @@ snapshots:
sirv: 3.0.2
structured-clone-es: 2.0.1
tinyglobby: 0.2.17
- unstorage: 1.17.5(@vercel/functions@3.9.5(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))
+ unstorage: 1.17.5(@vercel/functions@3.9.7(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))
vite: 8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)
vite-plugin-inspect: 11.4.1(@nuxt/kit@4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))))(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
vite-plugin-vue-tracer: 1.5.0(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
@@ -7720,30 +7885,30 @@ snapshots:
- utf-8-validate
- vue
- '@nuxt/eslint-config@1.17.0(@typescript-eslint/utils@8.67.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(@vue/compiler-sfc@3.5.41)(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
+ '@nuxt/eslint-config@1.17.0(@typescript-eslint/utils@8.67.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(@vue/compiler-sfc@3.5.41)(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
dependencies:
'@antfu/install-pkg': 2.0.1
'@clack/prompts': 1.7.0
- '@eslint/js': 10.0.1(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))
- '@nuxt/eslint-plugin': 1.17.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
- '@stylistic/eslint-plugin': 5.10.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))
- '@typescript-eslint/eslint-plugin': 8.67.0(@typescript-eslint/parser@8.67.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
- '@typescript-eslint/parser': 8.67.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
- eslint: 10.9.0(jiti@2.7.0)(supports-color@10.2.2)
- eslint-config-flat-gitignore: 2.3.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))
+ '@eslint/js': 10.0.1(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))
+ '@nuxt/eslint-plugin': 1.17.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
+ '@stylistic/eslint-plugin': 5.10.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))
+ '@typescript-eslint/eslint-plugin': 8.67.0(@typescript-eslint/parser@8.67.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
+ '@typescript-eslint/parser': 8.67.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
+ eslint: 10.10.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint-config-flat-gitignore: 2.3.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))
eslint-flat-config-utils: 3.2.0
- eslint-merge-processors: 2.0.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))
- eslint-plugin-import-lite: 0.6.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))
- eslint-plugin-import-x: 4.17.1(@typescript-eslint/utils@8.67.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)
- eslint-plugin-jsdoc: 63.3.3(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)
- eslint-plugin-regexp: 3.2.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))
- eslint-plugin-unicorn: 73.0.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))
- eslint-plugin-vue: 10.10.0(@stylistic/eslint-plugin@5.10.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2)))(@typescript-eslint/parser@8.67.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(vue-eslint-parser@10.4.1(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2))
- eslint-processor-vue-blocks: 2.0.0(@vue/compiler-sfc@3.5.41)(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))
+ eslint-merge-processors: 2.0.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))
+ eslint-plugin-import-lite: 0.6.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))
+ eslint-plugin-import-x: 4.17.1(@typescript-eslint/utils@8.67.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)
+ eslint-plugin-jsdoc: 63.3.3(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)
+ eslint-plugin-regexp: 3.2.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))
+ eslint-plugin-unicorn: 73.0.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))
+ eslint-plugin-vue: 10.10.0(@stylistic/eslint-plugin@5.10.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2)))(@typescript-eslint/parser@8.67.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(vue-eslint-parser@10.4.1(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2))
+ eslint-processor-vue-blocks: 2.0.0(@vue/compiler-sfc@3.5.41)(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))
globals: 17.11.0
local-pkg: 1.2.1
pathe: 2.0.3
- vue-eslint-parser: 10.4.1(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)
+ vue-eslint-parser: 10.4.1(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)
transitivePeerDependencies:
- '@typescript-eslint/utils'
- '@vue/compiler-sfc'
@@ -7751,22 +7916,22 @@ snapshots:
- supports-color
- typescript
- '@nuxt/eslint-plugin@1.17.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
+ '@nuxt/eslint-plugin@1.17.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
dependencies:
'@typescript-eslint/types': 8.67.0
- '@typescript-eslint/utils': 8.67.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
- eslint: 10.9.0(jiti@2.7.0)(supports-color@10.2.2)
+ '@typescript-eslint/utils': 8.67.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
+ eslint: 10.10.0(jiti@2.7.0)(supports-color@10.2.2)
transitivePeerDependencies:
- supports-color
- typescript
- '@nuxt/fonts@0.14.0(@vercel/functions@3.9.5(ws@8.21.3))(db0@0.3.4)(esbuild@0.28.2)(ioredis@5.11.1(supports-color@10.2.2))(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))':
+ '@nuxt/fonts@0.14.0(@vercel/functions@3.9.7(ws@8.21.3))(db0@0.3.4)(esbuild@0.28.2)(ioredis@5.11.1(supports-color@10.2.2))(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))':
dependencies:
'@nuxt/devtools-kit': 3.4.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
'@nuxt/kit': 4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))
consola: 3.4.2
defu: 6.1.7
- fontless: 0.2.1(@vercel/functions@3.9.5(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
+ fontless: 0.2.1(@vercel/functions@3.9.7(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
h3: 1.15.11
magic-regexp: 0.10.0
ofetch: 1.5.1
@@ -7776,7 +7941,7 @@ snapshots:
ufo: 1.6.4
unifont: 0.7.5
unplugin: 3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
- unstorage: 1.17.5(@vercel/functions@3.9.5(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))
+ unstorage: 1.17.5(@vercel/functions@3.9.7(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))
transitivePeerDependencies:
- '@azure/app-configuration'
- '@azure/cosmos'
@@ -7835,6 +8000,32 @@ snapshots:
- vite
- vue
+ '@nuxt/kit@3.21.11(magicast@0.5.4)':
+ dependencies:
+ c12: 3.3.4(magicast@0.5.4)
+ consola: 3.4.2
+ defu: 6.1.7
+ destr: 2.0.5
+ errx: 0.1.2
+ exsolve: 1.1.1
+ ignore: 7.0.6
+ jiti: 2.7.0
+ klona: 2.0.6
+ knitwork: 1.3.0
+ mlly: 1.8.2
+ ohash: 2.0.12
+ pathe: 2.0.3
+ pkg-types: 2.3.1
+ rc9: 3.0.1
+ scule: 1.3.0
+ semver: 7.8.5
+ tinyglobby: 0.2.17
+ ufo: 1.6.4
+ unctx: 2.5.0
+ untyped: 2.0.0
+ transitivePeerDependencies:
+ - magicast
+
'@nuxt/kit@4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))':
dependencies:
c12: 3.3.4(magicast@0.5.4)
@@ -7865,7 +8056,7 @@ snapshots:
- rolldown
- unplugin
- '@nuxt/nitro-server@4.5.2(80fa4b35290e6e68e37c82a4f05aec13)':
+ '@nuxt/nitro-server@4.5.2(188bc3c2c4363fd52825c16fd4cbef34)':
dependencies:
'@nuxt/devalue': 2.0.2
'@nuxt/kit': 4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))
@@ -7882,9 +8073,9 @@ snapshots:
impound: 1.1.7(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
klona: 2.0.6
mocked-exports: 0.1.1
- nitropack: 2.13.4(@vercel/functions@3.9.5(ws@8.21.3))(oxc-parser@0.143.0)(rolldown@1.2.5)(srvx@0.11.22)(supports-color@10.2.2)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
+ nitropack: 2.13.4(@vercel/functions@3.9.7(ws@8.21.3))(oxc-parser@0.143.0)(rolldown@1.2.5)(srvx@0.11.22)(supports-color@10.2.2)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
nostics: 1.2.0
- nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.5(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0)
+ nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.7(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0)
nypm: 0.6.9
ohash: 2.0.12
pathe: 2.0.3
@@ -7892,7 +8083,7 @@ snapshots:
std-env: 4.2.0
ufo: 1.6.4
unctx: 3.0.1(magic-string@1.2.3)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))
- unstorage: 1.17.5(@vercel/functions@3.9.5(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))
+ unstorage: 1.17.5(@vercel/functions@3.9.7(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))
vue: 3.5.41(typescript@6.0.3)
vue-bundle-renderer: 2.3.2
vue-devtools-stub: 0.1.0
@@ -7969,11 +8160,134 @@ snapshots:
rc9: 3.0.1
std-env: 4.2.0
- '@nuxt/ui@4.11.1(0716ad8e1ed9af83500c5a16dcdaaa58)':
+ '@nuxt/ui@4.11.0(4df280129450d67e9f51cc0909bcdbf1)':
dependencies:
'@floating-ui/dom': 1.8.0
'@iconify/vue': 5.0.1(vue@3.5.41(typescript@6.0.3))
- '@nuxt/fonts': 0.14.0(@vercel/functions@3.9.5(ws@8.21.3))(db0@0.3.4)(esbuild@0.28.2)(ioredis@5.11.1(supports-color@10.2.2))(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
+ '@nuxt/fonts': 0.14.0(@vercel/functions@3.9.7(ws@8.21.3))(db0@0.3.4)(esbuild@0.28.2)(ioredis@5.11.1(supports-color@10.2.2))(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
+ '@nuxt/icon': 2.5.1(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
+ '@nuxt/kit': 4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))
+ '@nuxt/schema': 4.5.2
+ '@nuxtjs/color-mode': 4.0.1(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))
+ '@standard-schema/spec': 1.1.0
+ '@tailwindcss/postcss': 4.3.3
+ '@tailwindcss/vite': 4.3.3(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
+ '@tanstack/vue-table': 8.21.3(vue@3.5.41(typescript@6.0.3))
+ '@tanstack/vue-virtual': 3.13.36(vue@3.5.41(typescript@6.0.3))
+ '@tiptap/core': 3.30.3(@tiptap/pm@3.30.3)
+ '@tiptap/extension-bubble-menu': 3.30.3(@tiptap/core@3.30.3(@tiptap/pm@3.30.3))(@tiptap/pm@3.30.3)
+ '@tiptap/extension-code': 3.30.3(@tiptap/core@3.30.3(@tiptap/pm@3.30.3))
+ '@tiptap/extension-collaboration': 3.30.3(@tiptap/core@3.30.3(@tiptap/pm@3.30.3))(@tiptap/pm@3.30.3)(@tiptap/y-tiptap@3.0.9(prosemirror-model@1.25.11)(prosemirror-state@1.4.4)(prosemirror-view@1.42.2)(y-protocols@1.0.7(yjs@13.6.32))(yjs@13.6.32))(yjs@13.6.32)
+ '@tiptap/extension-drag-handle': 3.30.3(@tiptap/core@3.30.3(@tiptap/pm@3.30.3))(@tiptap/extension-collaboration@3.30.3(@tiptap/core@3.30.3(@tiptap/pm@3.30.3))(@tiptap/pm@3.30.3)(@tiptap/y-tiptap@3.0.9(prosemirror-model@1.25.11)(prosemirror-state@1.4.4)(prosemirror-view@1.42.2)(y-protocols@1.0.7(yjs@13.6.32))(yjs@13.6.32))(yjs@13.6.32))(@tiptap/extension-node-range@3.30.3(@tiptap/core@3.30.3(@tiptap/pm@3.30.3))(@tiptap/pm@3.30.3))(@tiptap/pm@3.30.3)(@tiptap/y-tiptap@3.0.9(prosemirror-model@1.25.11)(prosemirror-state@1.4.4)(prosemirror-view@1.42.2)(y-protocols@1.0.7(yjs@13.6.32))(yjs@13.6.32))
+ '@tiptap/extension-drag-handle-vue-3': 3.30.3(@tiptap/extension-drag-handle@3.30.3(@tiptap/core@3.30.3(@tiptap/pm@3.30.3))(@tiptap/extension-collaboration@3.30.3(@tiptap/core@3.30.3(@tiptap/pm@3.30.3))(@tiptap/pm@3.30.3)(@tiptap/y-tiptap@3.0.9(prosemirror-model@1.25.11)(prosemirror-state@1.4.4)(prosemirror-view@1.42.2)(y-protocols@1.0.7(yjs@13.6.32))(yjs@13.6.32))(yjs@13.6.32))(@tiptap/extension-node-range@3.30.3(@tiptap/core@3.30.3(@tiptap/pm@3.30.3))(@tiptap/pm@3.30.3))(@tiptap/pm@3.30.3)(@tiptap/y-tiptap@3.0.9(prosemirror-model@1.25.11)(prosemirror-state@1.4.4)(prosemirror-view@1.42.2)(y-protocols@1.0.7(yjs@13.6.32))(yjs@13.6.32)))(@tiptap/pm@3.30.3)(@tiptap/vue-3@3.30.3(@floating-ui/dom@1.8.0)(@tiptap/core@3.30.3(@tiptap/pm@3.30.3))(@tiptap/pm@3.30.3)(vue@3.5.41(typescript@6.0.3)))(vue@3.5.41(typescript@6.0.3))
+ '@tiptap/extension-floating-menu': 3.30.3(@floating-ui/dom@1.8.0)(@tiptap/core@3.30.3(@tiptap/pm@3.30.3))(@tiptap/pm@3.30.3)
+ '@tiptap/extension-horizontal-rule': 3.30.3(@tiptap/core@3.30.3(@tiptap/pm@3.30.3))(@tiptap/pm@3.30.3)
+ '@tiptap/extension-image': 3.30.3(@tiptap/core@3.30.3(@tiptap/pm@3.30.3))
+ '@tiptap/extension-mention': 3.30.3(@tiptap/core@3.30.3(@tiptap/pm@3.30.3))(@tiptap/pm@3.30.3)(@tiptap/suggestion@3.30.3(@floating-ui/dom@1.8.0)(@tiptap/core@3.30.3(@tiptap/pm@3.30.3))(@tiptap/pm@3.30.3))
+ '@tiptap/extension-node-range': 3.30.3(@tiptap/core@3.30.3(@tiptap/pm@3.30.3))(@tiptap/pm@3.30.3)
+ '@tiptap/extension-placeholder': 3.30.3(@tiptap/extensions@3.30.3(@tiptap/core@3.30.3(@tiptap/pm@3.30.3))(@tiptap/pm@3.30.3))
+ '@tiptap/markdown': 3.30.3(@tiptap/core@3.30.3(@tiptap/pm@3.30.3))(@tiptap/pm@3.30.3)
+ '@tiptap/pm': 3.30.3
+ '@tiptap/starter-kit': 3.30.3
+ '@tiptap/suggestion': 3.30.3(@floating-ui/dom@1.8.0)(@tiptap/core@3.30.3(@tiptap/pm@3.30.3))(@tiptap/pm@3.30.3)
+ '@tiptap/vue-3': 3.30.3(@floating-ui/dom@1.8.0)(@tiptap/core@3.30.3(@tiptap/pm@3.30.3))(@tiptap/pm@3.30.3)(vue@3.5.41(typescript@6.0.3))
+ '@unhead/vue': 3.4.0(@oxc-project/types@0.146.0)(esbuild@0.28.2)(lightningcss@1.33.0)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
+ '@vueuse/core': 14.4.0(vue@3.5.41(typescript@6.0.3))
+ '@vueuse/integrations': 14.4.0(change-case@5.4.4)(fuse.js@7.5.0)(vue@3.5.41(typescript@6.0.3))
+ '@vueuse/shared': 14.4.0(vue@3.5.41(typescript@6.0.3))
+ colortranslator: 5.0.0
+ consola: 3.4.2
+ defu: 6.1.7
+ embla-carousel-auto-height: 8.6.0(embla-carousel@8.6.0)
+ embla-carousel-auto-scroll: 8.6.0(embla-carousel@8.6.0)
+ embla-carousel-autoplay: 8.6.0(embla-carousel@8.6.0)
+ embla-carousel-class-names: 8.6.0(embla-carousel@8.6.0)
+ embla-carousel-fade: 8.6.0(embla-carousel@8.6.0)
+ embla-carousel-vue: 8.6.0(vue@3.5.41(typescript@6.0.3))
+ embla-carousel-wheel-gestures: 8.1.0(embla-carousel@8.6.0)
+ fuse.js: 7.5.0
+ hookable: 6.1.1
+ knitwork: 1.3.0
+ magic-string: 1.2.3
+ mlly: 1.8.2
+ motion-v: 2.4.2(@vueuse/core@14.4.0(vue@3.5.41(typescript@6.0.3)))(vue@3.5.41(typescript@6.0.3))
+ ohash: 2.0.12
+ pathe: 2.0.3
+ reka-ui: 2.10.3(vue@3.5.41(typescript@6.0.3))
+ scule: 1.3.0
+ tailwind-merge: 3.6.0
+ tailwind-variants: 3.3.1(tailwind-merge@3.6.0)(tailwindcss@4.3.3)
+ tailwindcss: 4.3.3
+ tinyglobby: 0.2.17
+ typescript: 6.0.3
+ ufo: 1.6.4
+ unplugin: 3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
+ unplugin-auto-import: 21.1.0(@nuxt/kit@4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))))(@vueuse/core@14.4.0(vue@3.5.41(typescript@6.0.3)))(esbuild@0.28.2)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
+ unplugin-vue-components: 32.1.0(@nuxt/kit@4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))))(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
+ vaul-vue: 0.4.1(reka-ui@2.10.3(vue@3.5.41(typescript@6.0.3)))(vue@3.5.41(typescript@6.0.3))
+ vue-component-type-helpers: 3.3.11
+ optionalDependencies:
+ '@internationalized/date': 3.12.3
+ '@internationalized/number': 3.6.7
+ ai: 7.0.97(zod@4.6.1)
+ vue-router: 5.2.0(@vue/compiler-sfc@3.5.41)(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
+ zod: 4.6.1
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@farmfe/core'
+ - '@netlify/blobs'
+ - '@oxc-project/types'
+ - '@planetscale/database'
+ - '@rspack/core'
+ - '@unhead/cli'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/functions'
+ - '@vercel/kv'
+ - '@vitejs/devtools-kit'
+ - '@vue/composition-api'
+ - async-validator
+ - aws4fetch
+ - axios
+ - bun-types-no-globals
+ - change-case
+ - db0
+ - drauu
+ - embla-carousel
+ - esbuild
+ - focus-trap
+ - idb-keyval
+ - ioredis
+ - jwt-decode
+ - lightningcss
+ - magicast
+ - nprogress
+ - oxc-parser
+ - qrcode
+ - react
+ - react-dom
+ - rolldown
+ - rollup
+ - sortablejs
+ - universal-cookie
+ - unloader
+ - uploadthing
+ - vite
+ - vue
+ - webpack
+
+ '@nuxt/ui@4.11.1(f9711837b53e5eed9eacf00ad56e4c40)':
+ dependencies:
+ '@floating-ui/dom': 1.8.0
+ '@iconify/vue': 5.0.1(vue@3.5.41(typescript@6.0.3))
+ '@nuxt/fonts': 0.14.0(@vercel/functions@3.9.7(ws@8.21.3))(db0@0.3.4)(esbuild@0.28.2)(ioredis@5.11.1(supports-color@10.2.2))(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
'@nuxt/icon': 2.5.1(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
'@nuxt/kit': 4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))
'@nuxt/schema': 4.5.2
@@ -8038,9 +8352,8 @@ snapshots:
optionalDependencies:
'@internationalized/date': 3.12.3
'@internationalized/number': 3.6.7
- ai: 7.0.77(zod@4.4.3)
- vue-router: 5.2.0(@vue/compiler-sfc@3.5.41)(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
- zod: 4.4.3
+ ai: 7.0.97(zod@4.6.1)
+ zod: 4.6.1
transitivePeerDependencies:
- '@azure/app-configuration'
- '@azure/cosmos'
@@ -8092,7 +8405,7 @@ snapshots:
- vue
- webpack
- '@nuxt/vite-builder@4.5.2(5ea4d7f0831f7e722792e954b23a4f93)':
+ '@nuxt/vite-builder@4.5.2(26f4d21de89f1c50f9572a8528f34eef)':
dependencies:
'@nuxt/kit': 4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))
'@vitejs/plugin-vue': 6.0.8(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
@@ -8110,7 +8423,7 @@ snapshots:
knitwork: 1.3.0
mlly: 1.8.2
mocked-exports: 0.1.1
- nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.5(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0)
+ nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.7(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0)
nypm: 0.6.9
pathe: 2.0.3
pkg-types: 2.3.1
@@ -8123,7 +8436,7 @@ snapshots:
unplugin: 3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
vite: 8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)
vite-node: 6.0.0(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)
- vite-plugin-checker: 0.14.5(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(optionator@0.9.4)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))
+ vite-plugin-checker: 0.14.5(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(optionator@0.9.4)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))
vue: 3.5.41(typescript@6.0.3)
vue-bundle-renderer: 2.3.2
optionalDependencies:
@@ -8175,15 +8488,15 @@ snapshots:
- rolldown
- unplugin
- '@nuxtjs/mcp-toolkit@0.18.1(@vue/compiler-sfc@3.5.41)(h3@1.15.11)(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup@4.62.5)(supports-color@10.2.2)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))(zod@4.4.3)':
+ '@nuxtjs/mcp-toolkit@0.18.1(@vue/compiler-sfc@3.5.41)(h3@1.15.11)(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup@4.62.5)(supports-color@10.2.2)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))(zod@4.6.1)':
dependencies:
- '@modelcontextprotocol/sdk': 1.30.0(supports-color@10.2.2)(zod@4.4.3)
+ '@modelcontextprotocol/sdk': 1.30.0(supports-color@10.2.2)(zod@4.6.1)
'@nuxt/kit': 4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))
'@vitejs/plugin-vue': 6.0.8(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
h3: 1.15.11
tinyglobby: 0.2.17
vite-plugin-singlefile: 2.3.3(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
- zod: 4.4.3
+ zod: 4.6.1
optionalDependencies:
'@vue/compiler-sfc': 3.5.41
vite: 8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)
@@ -8198,20 +8511,20 @@ snapshots:
- supports-color
- unplugin
- '@nuxtjs/robots@6.2.0(3bc4744619a624c5169bc56293f3c985)':
+ '@nuxtjs/robots@6.2.0(9963a669bf37ac17e292d4f6f824546f)':
dependencies:
'@fingerprintjs/botd': 2.0.0
'@nuxt/kit': 4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))
consola: 3.4.2
defu: 6.1.7
h3: 1.15.11
- nuxt-site-config: 4.2.3(3bc4744619a624c5169bc56293f3c985)
- nuxtseo-shared: 5.3.14(fc4f002cf8929f5932e9df9bf8f19792)
+ nuxt-site-config: 4.2.3(9963a669bf37ac17e292d4f6f824546f)
+ nuxtseo-shared: 5.3.14(0f8908bb880cd164227784aaff03d6a3)
pathe: 2.0.3
pkg-types: 2.3.1
ufo: 1.6.4
optionalDependencies:
- zod: 4.4.3
+ zod: 4.6.1
transitivePeerDependencies:
- '@nuxt/schema'
- magic-string
@@ -8223,13 +8536,13 @@ snapshots:
- vite
- vue
- '@nuxtjs/sitemap@8.5.0(3bc4744619a624c5169bc56293f3c985)':
+ '@nuxtjs/sitemap@8.5.0(9963a669bf37ac17e292d4f6f824546f)':
dependencies:
'@nuxt/kit': 4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))
consola: 3.4.2
defu: 6.1.7
- nuxt-site-config: 4.2.3(3bc4744619a624c5169bc56293f3c985)
- nuxtseo-shared: 5.3.14(fc4f002cf8929f5932e9df9bf8f19792)
+ nuxt-site-config: 4.2.3(9963a669bf37ac17e292d4f6f824546f)
+ nuxtseo-shared: 5.3.14(0f8908bb880cd164227784aaff03d6a3)
ofetch: 1.5.1
pathe: 2.0.3
pkg-types: 2.3.1
@@ -8238,7 +8551,7 @@ snapshots:
ufo: 1.6.4
ultrahtml: 1.7.0
optionalDependencies:
- zod: 4.4.3
+ zod: 4.6.1
transitivePeerDependencies:
- '@nuxt/schema'
- magic-string
@@ -8258,7 +8571,7 @@ snapshots:
'@opentelemetry/api@1.9.1': {}
- '@opentelemetry/context-async-hooks@2.10.0(@opentelemetry/api@1.9.1)':
+ '@opentelemetry/context-async-hooks@2.11.0(@opentelemetry/api@1.9.1)':
dependencies:
'@opentelemetry/api': 1.9.1
@@ -8267,6 +8580,11 @@ snapshots:
'@opentelemetry/api': 1.9.1
'@opentelemetry/semantic-conventions': 1.43.0
+ '@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1)':
+ dependencies:
+ '@opentelemetry/api': 1.9.1
+ '@opentelemetry/semantic-conventions': 1.43.0
+
'@opentelemetry/exporter-trace-otlp-proto@0.221.0(@opentelemetry/api@1.9.1)':
dependencies:
'@opentelemetry/api': 1.9.1
@@ -8305,6 +8623,12 @@ snapshots:
'@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1)
'@opentelemetry/semantic-conventions': 1.43.0
+ '@opentelemetry/resources@2.11.0(@opentelemetry/api@1.9.1)':
+ dependencies:
+ '@opentelemetry/api': 1.9.1
+ '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1)
+ '@opentelemetry/semantic-conventions': 1.43.0
+
'@opentelemetry/sdk-logs@0.221.0(@opentelemetry/api@1.9.1)':
dependencies:
'@opentelemetry/api': 1.9.1
@@ -8319,20 +8643,20 @@ snapshots:
'@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1)
'@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1)
- '@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1)':
+ '@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1)':
dependencies:
'@opentelemetry/api': 1.9.1
- '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1)
- '@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1)
- '@opentelemetry/sdk-trace': 2.10.0(@opentelemetry/api@1.9.1)
+ '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1)
+ '@opentelemetry/resources': 2.11.0(@opentelemetry/api@1.9.1)
+ '@opentelemetry/sdk-trace': 2.11.0(@opentelemetry/api@1.9.1)
'@opentelemetry/semantic-conventions': 1.43.0
- '@opentelemetry/sdk-trace-node@2.10.0(@opentelemetry/api@1.9.1)':
+ '@opentelemetry/sdk-trace-node@2.11.0(@opentelemetry/api@1.9.1)':
dependencies:
'@opentelemetry/api': 1.9.1
- '@opentelemetry/context-async-hooks': 2.10.0(@opentelemetry/api@1.9.1)
- '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1)
- '@opentelemetry/sdk-trace-base': 2.10.0(@opentelemetry/api@1.9.1)
+ '@opentelemetry/context-async-hooks': 2.11.0(@opentelemetry/api@1.9.1)
+ '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1)
+ '@opentelemetry/sdk-trace-base': 2.11.0(@opentelemetry/api@1.9.1)
'@opentelemetry/sdk-trace@2.10.0(@opentelemetry/api@1.9.1)':
dependencies:
@@ -8341,6 +8665,13 @@ snapshots:
'@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1)
'@opentelemetry/semantic-conventions': 1.43.0
+ '@opentelemetry/sdk-trace@2.11.0(@opentelemetry/api@1.9.1)':
+ dependencies:
+ '@opentelemetry/api': 1.9.1
+ '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1)
+ '@opentelemetry/resources': 2.11.0(@opentelemetry/api@1.9.1)
+ '@opentelemetry/semantic-conventions': 1.43.0
+
'@opentelemetry/semantic-conventions@1.43.0': {}
'@oxc-parser/binding-android-arm-eabi@0.143.0':
@@ -8721,13 +9052,15 @@ snapshots:
'@speed-highlight/core@1.2.24': {}
+ '@sqlite.org/sqlite-wasm@3.53.0-build1': {}
+
'@standard-schema/spec@1.1.0': {}
- '@stylistic/eslint-plugin@5.10.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))':
+ '@stylistic/eslint-plugin@5.10.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))':
dependencies:
- '@eslint-community/eslint-utils': 4.10.1(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))
+ '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))
'@typescript-eslint/types': 8.67.0
- eslint: 10.9.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.10.0(jiti@2.7.0)(supports-color@10.2.2)
eslint-visitor-keys: 4.2.1
espree: 10.4.0
estraverse: 5.3.0
@@ -9098,15 +9431,15 @@ snapshots:
'@types/web-bluetooth@0.0.21': {}
- '@typescript-eslint/eslint-plugin@8.67.0(@typescript-eslint/parser@8.67.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
+ '@typescript-eslint/eslint-plugin@8.67.0(@typescript-eslint/parser@8.67.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
- '@typescript-eslint/parser': 8.67.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
+ '@typescript-eslint/parser': 8.67.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/scope-manager': 8.67.0
- '@typescript-eslint/type-utils': 8.67.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
- '@typescript-eslint/utils': 8.67.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
+ '@typescript-eslint/type-utils': 8.67.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
+ '@typescript-eslint/utils': 8.67.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/visitor-keys': 8.67.0
- eslint: 10.9.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.10.0(jiti@2.7.0)(supports-color@10.2.2)
ignore: 7.0.6
natural-compare: 1.4.0
ts-api-utils: 2.5.0(typescript@6.0.3)
@@ -9114,14 +9447,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/parser@8.67.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
+ '@typescript-eslint/parser@8.67.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
dependencies:
'@typescript-eslint/scope-manager': 8.67.0
'@typescript-eslint/types': 8.67.0
'@typescript-eslint/typescript-estree': 8.67.0(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/visitor-keys': 8.67.0
debug: 4.4.3(supports-color@10.2.2)
- eslint: 10.9.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.10.0(jiti@2.7.0)(supports-color@10.2.2)
typescript: 6.0.3
transitivePeerDependencies:
- supports-color
@@ -9144,13 +9477,13 @@ snapshots:
dependencies:
typescript: 6.0.3
- '@typescript-eslint/type-utils@8.67.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
+ '@typescript-eslint/type-utils@8.67.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
dependencies:
'@typescript-eslint/types': 8.67.0
'@typescript-eslint/typescript-estree': 8.67.0(supports-color@10.2.2)(typescript@6.0.3)
- '@typescript-eslint/utils': 8.67.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
+ '@typescript-eslint/utils': 8.67.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
debug: 4.4.3(supports-color@10.2.2)
- eslint: 10.9.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.10.0(jiti@2.7.0)(supports-color@10.2.2)
ts-api-utils: 2.5.0(typescript@6.0.3)
typescript: 6.0.3
transitivePeerDependencies:
@@ -9173,13 +9506,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/utils@8.67.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
+ '@typescript-eslint/utils@8.67.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
dependencies:
- '@eslint-community/eslint-utils': 4.10.1(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))
+ '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))
'@typescript-eslint/scope-manager': 8.67.0
'@typescript-eslint/types': 8.67.0
'@typescript-eslint/typescript-estree': 8.67.0(supports-color@10.2.2)(typescript@6.0.3)
- eslint: 10.9.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.10.0(jiti@2.7.0)(supports-color@10.2.2)
typescript: 6.0.3
transitivePeerDependencies:
- supports-color
@@ -9304,12 +9637,12 @@ snapshots:
'@unrs/resolver-binding-win32-x64-msvc@1.12.2':
optional: true
- '@vercel/analytics@2.0.1(nuxt@4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.5(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))':
+ '@vercel/analytics@2.0.1(nuxt@4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.7(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))':
optionalDependencies:
- nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.5(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0)
+ nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.7(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0)
vue: 3.5.41(typescript@6.0.3)
- '@vercel/cli-config@0.2.4':
+ '@vercel/cli-config@0.2.6':
dependencies:
xdg-app-paths: 5.5.1
zod: 4.1.11
@@ -9318,9 +9651,9 @@ snapshots:
dependencies:
execa: 5.1.1
- '@vercel/functions@3.9.5(ws@8.21.3)':
+ '@vercel/functions@3.9.7(ws@8.21.3)':
dependencies:
- '@vercel/oidc': 3.8.5
+ '@vercel/oidc': 3.8.7
optionalDependencies:
ws: 8.21.3
@@ -9353,25 +9686,25 @@ snapshots:
'@vercel/oidc@3.2.0': {}
- '@vercel/oidc@3.8.5':
+ '@vercel/oidc@3.8.7':
dependencies:
- '@vercel/cli-config': 0.2.4
+ '@vercel/cli-config': 0.2.6
'@vercel/cli-exec': 1.0.1
jose: 5.10.0
- '@vercel/otel@2.1.3(@opentelemetry/api-logs@0.221.0)(@opentelemetry/api@1.9.1)(@opentelemetry/instrumentation@0.221.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2))(@opentelemetry/resources@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-logs@0.221.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-metrics@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1))':
+ '@vercel/otel@2.1.3(@opentelemetry/api-logs@0.221.0)(@opentelemetry/api@1.9.1)(@opentelemetry/instrumentation@0.221.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2))(@opentelemetry/resources@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-logs@0.221.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-metrics@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1))':
dependencies:
'@opentelemetry/api': 1.9.1
'@opentelemetry/api-logs': 0.221.0
'@opentelemetry/instrumentation': 0.221.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)
- '@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1)
+ '@opentelemetry/resources': 2.11.0(@opentelemetry/api@1.9.1)
'@opentelemetry/sdk-logs': 0.221.0(@opentelemetry/api@1.9.1)
'@opentelemetry/sdk-metrics': 2.10.0(@opentelemetry/api@1.9.1)
- '@opentelemetry/sdk-trace-base': 2.10.0(@opentelemetry/api@1.9.1)
+ '@opentelemetry/sdk-trace-base': 2.11.0(@opentelemetry/api@1.9.1)
- '@vercel/speed-insights@2.0.0(nuxt@4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.5(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))':
+ '@vercel/speed-insights@2.0.0(nuxt@4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.7(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))':
optionalDependencies:
- nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.5(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0)
+ nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.7(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0)
vue: 3.5.41(typescript@6.0.3)
'@vitejs/plugin-vue-jsx@5.1.6(supports-color@10.2.2)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))':
@@ -9599,13 +9932,13 @@ snapshots:
'@vueuse/metadata@14.4.0': {}
- '@vueuse/nuxt@14.4.0(fc4d6faa55285c3ac9a966d10279f90e)':
+ '@vueuse/nuxt@14.4.0(400f64eee5a6ea31a3859bc8b5e58b0e)':
dependencies:
'@nuxt/kit': 4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))
'@vueuse/core': 14.4.0(vue@3.5.41(typescript@6.0.3))
'@vueuse/metadata': 14.4.0
local-pkg: 1.2.1
- nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.5(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0)
+ nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.7(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0)
vue: 3.5.41(typescript@6.0.3)
transitivePeerDependencies:
- magic-string
@@ -9650,12 +9983,12 @@ snapshots:
agent-base@7.1.4: {}
- ai@7.0.77(zod@4.4.3):
+ ai@7.0.97(zod@4.6.1):
dependencies:
- '@ai-sdk/gateway': 4.0.62(zod@4.4.3)
- '@ai-sdk/provider': 4.0.7
- '@ai-sdk/provider-utils': 5.0.29(zod@4.4.3)
- zod: 4.4.3
+ '@ai-sdk/gateway': 4.0.78(zod@4.6.1)
+ '@ai-sdk/provider': 4.0.13
+ '@ai-sdk/provider-utils': 5.0.39(zod@4.6.1)
+ zod: 4.6.1
ajv-formats@3.0.1(ajv@8.20.0):
optionalDependencies:
@@ -9880,6 +10213,14 @@ snapshots:
cac@7.0.0: {}
+ cacheable@2.5.0:
+ dependencies:
+ '@cacheable/memory': 2.2.0
+ '@cacheable/utils': 2.5.0
+ hookified: 1.15.1
+ keyv: 5.6.0
+ qified: 0.10.1
+
call-bind-apply-helpers@1.0.2:
dependencies:
es-errors: 1.3.0
@@ -9950,7 +10291,7 @@ snapshots:
colortranslator@5.0.0: {}
- comark-content@https://pkg.pr.new/comark-content@baefd4d(@vercel/functions@3.9.5(ws@8.21.3))(beautiful-mermaid@1.1.3)(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))(rangi@2.2.0)(shiki@4.4.3):
+ comark-content@0.4.0(@vercel/functions@3.9.7(ws@8.21.3))(beautiful-mermaid@1.1.3)(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))(rangi@2.2.0)(shiki@4.4.3):
dependencies:
citty: 0.2.2
comark: 0.6.2(beautiful-mermaid@1.1.3)(rangi@2.2.0)(shiki@4.4.3)
@@ -9961,7 +10302,7 @@ snapshots:
picomatch: 4.0.5
slugify: 1.6.9
ufo: 1.6.4
- unstorage: 1.17.5(@vercel/functions@3.9.5(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))
+ unstorage: 1.17.5(@vercel/functions@3.9.7(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))
transitivePeerDependencies:
- '@azure/app-configuration'
- '@azure/cosmos'
@@ -9991,7 +10332,7 @@ snapshots:
dependencies:
entities: 8.0.0
htmlparser2: 12.0.0
- js-yaml: 5.3.0
+ js-yaml: 5.4.1
markdown-exit: 1.1.0-beta.2
optionalDependencies:
beautiful-mermaid: 1.1.3
@@ -10406,10 +10747,10 @@ snapshots:
escape-string-regexp@5.0.0: {}
- eslint-config-flat-gitignore@2.3.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2)):
+ eslint-config-flat-gitignore@2.3.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2)):
dependencies:
- '@eslint/compat': 2.1.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))
- eslint: 10.9.0(jiti@2.7.0)(supports-color@10.2.2)
+ '@eslint/compat': 2.1.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))
+ eslint: 10.10.0(jiti@2.7.0)(supports-color@10.2.2)
eslint-flat-config-utils@3.2.0:
dependencies:
@@ -10423,20 +10764,20 @@ snapshots:
optionalDependencies:
unrs-resolver: 1.12.2
- eslint-merge-processors@2.0.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2)):
+ eslint-merge-processors@2.0.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2)):
dependencies:
- eslint: 10.9.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.10.0(jiti@2.7.0)(supports-color@10.2.2)
- eslint-plugin-import-lite@0.6.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2)):
+ eslint-plugin-import-lite@0.6.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2)):
dependencies:
- eslint: 10.9.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.10.0(jiti@2.7.0)(supports-color@10.2.2)
- eslint-plugin-import-x@4.17.1(@typescript-eslint/utils@8.67.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2):
+ eslint-plugin-import-x@4.17.1(@typescript-eslint/utils@8.67.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2):
dependencies:
'@typescript-eslint/types': 8.67.0
comment-parser: 1.4.8
debug: 4.4.3(supports-color@10.2.2)
- eslint: 10.9.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.10.0(jiti@2.7.0)(supports-color@10.2.2)
eslint-import-context: 0.1.9(unrs-resolver@1.12.2)
is-glob: 4.0.3
minimatch: 10.2.6
@@ -10444,11 +10785,11 @@ snapshots:
stable-hash-x: 0.2.0
unrs-resolver: 1.12.2
optionalDependencies:
- '@typescript-eslint/utils': 8.67.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
+ '@typescript-eslint/utils': 8.67.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
transitivePeerDependencies:
- supports-color
- eslint-plugin-jsdoc@63.3.3(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2):
+ eslint-plugin-jsdoc@63.3.3(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2):
dependencies:
'@es-joy/jsdoccomment': 0.91.0
'@es-joy/resolve.exports': 1.2.0
@@ -10456,7 +10797,7 @@ snapshots:
comment-parser: 1.4.7
debug: 4.4.3(supports-color@10.2.2)
escape-string-regexp: 4.0.0
- eslint: 10.9.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.10.0(jiti@2.7.0)(supports-color@10.2.2)
espree: 11.2.0
esquery: 1.7.0
html-entities: 2.6.0
@@ -10468,20 +10809,20 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-plugin-regexp@3.2.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2)):
+ eslint-plugin-regexp@3.2.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2)):
dependencies:
- '@eslint-community/eslint-utils': 4.10.1(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))
+ '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))
'@eslint-community/regexpp': 4.12.2
comment-parser: 1.4.8
- eslint: 10.9.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.10.0(jiti@2.7.0)(supports-color@10.2.2)
jsdoc-type-pratt-parser: 9.2.0
refa: 0.12.1
regexp-ast-analysis: 0.7.1
scslre: 0.3.0
- eslint-plugin-unicorn@73.0.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2)):
+ eslint-plugin-unicorn@73.0.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2)):
dependencies:
- '@eslint-community/eslint-utils': 4.10.1(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))
+ '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))
'@eslint/css-tree': 4.0.5
browserslist: 4.28.8
change-case: 5.4.4
@@ -10489,7 +10830,7 @@ snapshots:
core-js-compat: 3.50.0
detect-indent: 7.0.2
entities: 4.5.0
- eslint: 10.9.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.10.0(jiti@2.7.0)(supports-color@10.2.2)
find-up-simple: 1.0.1
globals: 17.11.0
indent-string: 5.0.0
@@ -10503,24 +10844,24 @@ snapshots:
strip-indent: 4.1.1
yaml: 2.9.0
- eslint-plugin-vue@10.10.0(@stylistic/eslint-plugin@5.10.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2)))(@typescript-eslint/parser@8.67.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(vue-eslint-parser@10.4.1(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)):
+ eslint-plugin-vue@10.10.0(@stylistic/eslint-plugin@5.10.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2)))(@typescript-eslint/parser@8.67.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(vue-eslint-parser@10.4.1(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)):
dependencies:
- '@eslint-community/eslint-utils': 4.10.1(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))
- eslint: 10.9.0(jiti@2.7.0)(supports-color@10.2.2)
+ '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))
+ eslint: 10.10.0(jiti@2.7.0)(supports-color@10.2.2)
natural-compare: 1.4.0
nth-check: 2.1.1
postcss-selector-parser: 7.1.5
semver: 7.8.5
- vue-eslint-parser: 10.4.1(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)
+ vue-eslint-parser: 10.4.1(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)
xml-name-validator: 5.0.0
optionalDependencies:
- '@stylistic/eslint-plugin': 5.10.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))
- '@typescript-eslint/parser': 8.67.0(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
+ '@stylistic/eslint-plugin': 5.10.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))
+ '@typescript-eslint/parser': 8.67.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
- eslint-processor-vue-blocks@2.0.0(@vue/compiler-sfc@3.5.41)(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2)):
+ eslint-processor-vue-blocks@2.0.0(@vue/compiler-sfc@3.5.41)(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2)):
dependencies:
'@vue/compiler-sfc': 3.5.41
- eslint: 10.9.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.10.0(jiti@2.7.0)(supports-color@10.2.2)
eslint-scope@9.1.2:
dependencies:
@@ -10535,14 +10876,14 @@ snapshots:
eslint-visitor-keys@5.0.1: {}
- eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2):
+ eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2):
dependencies:
- '@eslint-community/eslint-utils': 4.10.1(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))
+ '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))
'@eslint-community/regexpp': 4.12.2
'@eslint/config-array': 0.23.5(supports-color@10.2.2)
'@eslint/config-helpers': 0.7.0
'@eslint/core': 1.2.1
- '@eslint/plugin-kit': 0.7.2
+ '@eslint/plugin-kit': 0.7.3
'@humanfs/node': 0.16.8
'@humanwhocodes/module-importer': 1.0.1
'@humanwhocodes/retry': 0.4.3
@@ -10557,7 +10898,7 @@ snapshots:
esquery: 1.7.0
esutils: 2.0.3
fast-deep-equal: 3.1.3
- file-entry-cache: 8.0.0
+ file-entry-cache: 11.1.5
find-up: 5.0.0
glob-parent: 6.0.2
ignore: 5.3.2
@@ -10745,9 +11086,9 @@ snapshots:
fflate@0.7.5: {}
- file-entry-cache@8.0.0:
+ file-entry-cache@11.1.5:
dependencies:
- flat-cache: 4.0.1
+ flat-cache: 6.1.23
file-uri-to-path@1.0.0: {}
@@ -10773,10 +11114,11 @@ snapshots:
locate-path: 6.0.0
path-exists: 4.0.0
- flat-cache@4.0.1:
+ flat-cache@6.1.23:
dependencies:
+ cacheable: 2.5.0
flatted: 3.4.4
- keyv: 4.5.4
+ hookified: 1.15.1
flatted@3.4.4: {}
@@ -10796,7 +11138,7 @@ snapshots:
dependencies:
tiny-inflate: 1.0.3
- fontless@0.2.1(@vercel/functions@3.9.5(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)):
+ fontless@0.2.1(@vercel/functions@3.9.7(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)):
dependencies:
consola: 3.4.2
css-tree: 3.2.1
@@ -10810,7 +11152,7 @@ snapshots:
pathe: 2.0.3
ufo: 1.6.4
unifont: 0.7.5
- unstorage: 1.17.5(@vercel/functions@3.9.5(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))
+ unstorage: 1.17.5(@vercel/functions@3.9.7(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))
optionalDependencies:
vite: 8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)
transitivePeerDependencies:
@@ -10963,6 +11305,10 @@ snapshots:
has-symbols@1.1.0: {}
+ hashery@1.5.1:
+ dependencies:
+ hookified: 1.15.1
+
hasown@2.0.4:
dependencies:
function-bind: 1.1.2
@@ -10995,6 +11341,10 @@ snapshots:
hookable@6.1.1: {}
+ hookified@1.15.1: {}
+
+ hookified@2.2.0: {}
+
html-entities@2.6.0: {}
html-void-elements@3.0.0: {}
@@ -11183,7 +11533,7 @@ snapshots:
js-tokens@4.0.0: {}
- js-yaml@5.3.0:
+ js-yaml@5.4.1:
dependencies:
argparse: 2.0.1
@@ -11195,8 +11545,6 @@ snapshots:
jsesc@3.1.0: {}
- json-buffer@3.0.1: {}
-
json-schema-traverse@0.4.1: {}
json-schema-traverse@1.0.0: {}
@@ -11209,9 +11557,9 @@ snapshots:
json5@2.2.3: {}
- keyv@4.5.4:
+ keyv@5.6.0:
dependencies:
- json-buffer: 3.0.1
+ '@keyv/serialize': 1.1.1
kleur@4.1.5: {}
@@ -11575,7 +11923,7 @@ snapshots:
dependencies:
content-type: 2.1.0
- nitropack@2.13.4(@vercel/functions@3.9.5(ws@8.21.3))(oxc-parser@0.143.0)(rolldown@1.2.5)(srvx@0.11.22)(supports-color@10.2.2)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)):
+ nitropack@2.13.4(@vercel/functions@3.9.7(ws@8.21.3))(oxc-parser@0.143.0)(rolldown@1.2.5)(srvx@0.11.22)(supports-color@10.2.2)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)):
dependencies:
'@cloudflare/kv-asset-handler': 0.4.2
'@rollup/plugin-alias': 6.0.0(rollup@4.62.5)
@@ -11642,7 +11990,7 @@ snapshots:
unenv: 2.0.0-rc.24
unimport: 6.4.0(esbuild@0.28.2)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
unplugin-utils: 0.3.2
- unstorage: 1.17.5(@vercel/functions@3.9.5(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))
+ unstorage: 1.17.5(@vercel/functions@3.9.7(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))
untyped: 2.0.0
unwasm: 0.5.3
youch: 4.1.1
@@ -11726,7 +12074,7 @@ snapshots:
dependencies:
boolbase: 1.0.0
- nuxt-agent-discovery@0.5.1(3adf33016b78b1d9f3d0bf8940d45a28):
+ nuxt-agent-discovery@0.5.1(93fae588a377ff5ba3fd984718cb8b37):
dependencies:
'@nuxt/kit': 4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))
defu: 6.1.7
@@ -11734,9 +12082,9 @@ snapshots:
ufo: 1.6.4
yaml: 2.9.0
optionalDependencies:
- '@nuxtjs/mcp-toolkit': 0.18.1(@vue/compiler-sfc@3.5.41)(h3@1.15.11)(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup@4.62.5)(supports-color@10.2.2)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))(zod@4.4.3)
- '@nuxtjs/robots': 6.2.0(3bc4744619a624c5169bc56293f3c985)
- '@nuxtjs/sitemap': 8.5.0(3bc4744619a624c5169bc56293f3c985)
+ '@nuxtjs/mcp-toolkit': 0.18.1(@vue/compiler-sfc@3.5.41)(h3@1.15.11)(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup@4.62.5)(supports-color@10.2.2)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))(zod@4.6.1)
+ '@nuxtjs/robots': 6.2.0(9963a669bf37ac17e292d4f6f824546f)
+ '@nuxtjs/sitemap': 8.5.0(9963a669bf37ac17e292d4f6f824546f)
comark: 0.6.2(beautiful-mermaid@1.1.3)(rangi@2.2.0)(shiki@4.4.3)
nuxt-llms: https://pkg.pr.new/nuxt-content/nuxt-llms/nuxt-llms@f6a9730(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))
transitivePeerDependencies:
@@ -11756,7 +12104,7 @@ snapshots:
- rolldown
- unplugin
- nuxt-og-image@6.7.8(d69daeae2668f4062a5626b724092832):
+ nuxt-og-image@6.7.8(02a9fd06dc986c7b60d00a5f031b9e09):
dependencies:
'@clack/prompts': 1.7.0
'@nuxt/kit': 4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))
@@ -11772,8 +12120,8 @@ snapshots:
magic-string: 1.2.3
magicast: 0.5.4
mocked-exports: 0.1.1
- nuxt-site-config: 4.2.3(3bc4744619a624c5169bc56293f3c985)
- nuxtseo-shared: 5.3.14(fc4f002cf8929f5932e9df9bf8f19792)
+ nuxt-site-config: 4.2.3(9963a669bf37ac17e292d4f6f824546f)
+ nuxtseo-shared: 5.3.14(0f8908bb880cd164227784aaff03d6a3)
nypm: 0.6.9
object-identity: 0.2.3
ofetch: 1.5.1
@@ -11789,15 +12137,15 @@ snapshots:
ufo: 1.6.4
ultrahtml: 1.7.0
unplugin: 3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
- unstorage: 1.17.5(@vercel/functions@3.9.5(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))
+ unstorage: 1.17.5(@vercel/functions@3.9.7(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))
optionalDependencies:
'@resvg/resvg-js': 2.6.2
- fontless: 0.2.1(@vercel/functions@3.9.5(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
- nitropack: 2.13.4(@vercel/functions@3.9.5(ws@8.21.3))(oxc-parser@0.143.0)(rolldown@1.2.5)(srvx@0.11.22)(supports-color@10.2.2)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
+ fontless: 0.2.1(@vercel/functions@3.9.7(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
+ nitropack: 2.13.4(@vercel/functions@3.9.7(ws@8.21.3))(oxc-parser@0.143.0)(rolldown@1.2.5)(srvx@0.11.22)(supports-color@10.2.2)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
satori: 0.29.1
tailwindcss: 4.3.3
unifont: 0.7.5
- zod: 4.4.3
+ zod: 4.6.1
transitivePeerDependencies:
- '@farmfe/core'
- '@nuxt/schema'
@@ -11814,18 +12162,18 @@ snapshots:
- vue
- webpack
- nuxt-schema-org@6.3.1(922ee4371f2ec5825b5f186a85055e54):
+ nuxt-schema-org@6.3.1(374ede1556afd53e77a90e7d78db3c9d):
dependencies:
'@nuxt/kit': 4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))
defu: 6.1.7
- nuxt-site-config: 4.2.3(3bc4744619a624c5169bc56293f3c985)
- nuxtseo-shared: 5.3.14(fc4f002cf8929f5932e9df9bf8f19792)
+ nuxt-site-config: 4.2.3(9963a669bf37ac17e292d4f6f824546f)
+ nuxtseo-shared: 5.3.14(0f8908bb880cd164227784aaff03d6a3)
pkg-types: 2.3.1
ufo: 1.6.4
optionalDependencies:
'@unhead/vue': 3.4.0(@oxc-project/types@0.146.0)(esbuild@0.28.2)(lightningcss@1.33.0)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
unhead: 3.4.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
- zod: 4.4.3
+ zod: 4.6.1
transitivePeerDependencies:
- '@nuxt/schema'
- magic-string
@@ -11837,7 +12185,7 @@ snapshots:
- vite
- vue
- nuxt-seo-utils@8.4.2(6c2732a7424285fd39f1e74aab3b66f7):
+ nuxt-seo-utils@8.5.0(0e3d028a172b021df1bc33f9e04f4227):
dependencies:
'@nuxt/kit': 4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))
citty: 0.2.2
@@ -11845,9 +12193,9 @@ snapshots:
defu: 6.1.7
escape-string-regexp: 5.0.0
exsolve: 1.1.1
- nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.5(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0)
- nuxt-site-config: 4.2.3(3bc4744619a624c5169bc56293f3c985)
- nuxtseo-shared: 5.3.14(fc4f002cf8929f5932e9df9bf8f19792)
+ nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.7(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0)
+ nuxt-site-config: 4.2.3(9963a669bf37ac17e292d4f6f824546f)
+ nuxtseo-shared: 5.3.14(0f8908bb880cd164227784aaff03d6a3)
pathe: 2.0.3
pkg-types: 2.3.1
scule: 1.3.0
@@ -11858,7 +12206,7 @@ snapshots:
esbuild: 0.28.2
lightningcss: 1.33.0
rolldown: 1.2.5
- sharp: 0.35.3(@types/node@26.2.0)
+ sharp: 0.35.4(@types/node@26.2.0)
unhead: 3.4.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
transitivePeerDependencies:
- '@nuxt/schema'
@@ -11885,7 +12233,7 @@ snapshots:
- unplugin
- vue
- nuxt-site-config@4.2.3(3bc4744619a624c5169bc56293f3c985):
+ nuxt-site-config@4.2.3(9963a669bf37ac17e292d4f6f824546f):
dependencies:
'@nuxt/devalue': 2.0.2
'@nuxt/kit': 4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))
@@ -11893,7 +12241,7 @@ snapshots:
defu: 6.1.7
h3: 1.15.11
nuxt-site-config-kit: 4.2.3(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))(vue@3.5.41(typescript@6.0.3))
- nuxtseo-shared: 5.3.14(fc4f002cf8929f5932e9df9bf8f19792)
+ nuxtseo-shared: 5.3.14(0f8908bb880cd164227784aaff03d6a3)
pathe: 2.0.3
pkg-types: 2.3.1
site-config-stack: 4.2.3(vue@3.5.41(typescript@6.0.3))
@@ -11910,16 +12258,27 @@ snapshots:
- vite
- zod
- nuxt@4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.5(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0):
+ nuxt-workers@0.1.0(magicast@0.5.4):
+ dependencies:
+ '@nuxt/kit': 3.21.11(magicast@0.5.4)
+ magic-string: 0.30.21
+ mlly: 1.8.2
+ pathe: 2.0.3
+ ufo: 1.6.4
+ unplugin: 2.3.11
+ transitivePeerDependencies:
+ - magicast
+
+ nuxt@4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.7(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0):
dependencies:
'@dxup/nuxt': 0.5.10(esbuild@0.28.2)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
'@nuxt/cli': 3.37.0(@nuxt/schema@4.5.2)(magicast@0.5.4)(supports-color@10.2.2)
- '@nuxt/devtools': 3.4.2(@vercel/functions@3.9.5(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))(magic-string@1.2.3)(oxc-parser@0.143.0)(rolldown@1.2.5)(supports-color@10.2.2)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
+ '@nuxt/devtools': 3.4.2(@vercel/functions@3.9.7(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))(magic-string@1.2.3)(oxc-parser@0.143.0)(rolldown@1.2.5)(supports-color@10.2.2)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
'@nuxt/kit': 4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))
- '@nuxt/nitro-server': 4.5.2(80fa4b35290e6e68e37c82a4f05aec13)
+ '@nuxt/nitro-server': 4.5.2(188bc3c2c4363fd52825c16fd4cbef34)
'@nuxt/schema': 4.5.2
'@nuxt/telemetry': 2.8.0(@nuxt/kit@4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))))
- '@nuxt/vite-builder': 4.5.2(5ea4d7f0831f7e722792e954b23a4f93)
+ '@nuxt/vite-builder': 4.5.2(26f4d21de89f1c50f9572a8528f34eef)
'@unhead/vue': 3.4.0(@oxc-project/types@0.146.0)(esbuild@0.28.2)(lightningcss@1.33.0)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
'@vue/shared': 3.5.41
chokidar: 5.0.0
@@ -12051,17 +12410,17 @@ snapshots:
- xml2js
- yaml
- nuxtseo-layer-devtools@5.3.14(9f33b9b9e7e20aae9f2d2c74340473d1):
+ nuxtseo-layer-devtools@5.3.14(ca9ba60081d8b5129f3ddf17535a8092):
dependencies:
'@iconify-json/carbon': 1.2.25
'@nuxt/devtools-kit': 4.0.0-alpha.7(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
'@nuxt/kit': 4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))
- '@nuxt/ui': 4.11.1(0716ad8e1ed9af83500c5a16dcdaaa58)
+ '@nuxt/ui': 4.11.0(4df280129450d67e9f51cc0909bcdbf1)
'@shikijs/langs': 4.4.3
'@shikijs/themes': 4.4.3
'@vueuse/core': 14.4.0(vue@3.5.41(typescript@6.0.3))
- '@vueuse/nuxt': 14.4.0(fc4d6faa55285c3ac9a966d10279f90e)
- nuxtseo-shared: 5.3.14(fc4f002cf8929f5932e9df9bf8f19792)
+ '@vueuse/nuxt': 14.4.0(400f64eee5a6ea31a3859bc8b5e58b0e)
+ nuxtseo-shared: 5.3.14(0f8908bb880cd164227784aaff03d6a3)
ofetch: 1.5.1
shiki: 4.4.3
tailwindcss: 4.3.3
@@ -12151,7 +12510,7 @@ snapshots:
- yup
- zod
- nuxtseo-shared@5.3.14(fc4f002cf8929f5932e9df9bf8f19792):
+ nuxtseo-shared@5.3.14(0f8908bb880cd164227784aaff03d6a3):
dependencies:
'@clack/prompts': 1.7.0
'@nuxt/devtools-kit': 4.0.0-alpha.7(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.5)(rollup@4.62.5)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)))(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))
@@ -12160,7 +12519,7 @@ snapshots:
birpc: 4.2.0
consola: 3.4.2
defu: 6.1.7
- nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.5(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0)
+ nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.146.0)(@types/node@26.2.0)(@vercel/functions@3.9.7(ws@8.21.3))(@vue/compiler-sfc@3.5.41)(db0@0.3.4)(esbuild@0.28.2)(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(optionator@0.9.4)(oxc-parser@0.143.0)(rolldown@1.2.5)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.5)(rollup@4.62.5))(rollup@4.62.5)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.50.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.0)
nypm: 0.6.9
ofetch: 1.5.1
pathe: 2.0.3
@@ -12171,8 +12530,8 @@ snapshots:
ufo: 1.6.4
vue: 3.5.41(typescript@6.0.3)
optionalDependencies:
- nuxt-site-config: 4.2.3(3bc4744619a624c5169bc56293f3c985)
- zod: 4.4.3
+ nuxt-site-config: 4.2.3(9963a669bf37ac17e292d4f6f824546f)
+ zod: 4.6.1
transitivePeerDependencies:
- magic-string
- magicast
@@ -12635,6 +12994,10 @@ snapshots:
punycode@2.3.1: {}
+ qified@0.10.1:
+ dependencies:
+ hookified: 2.2.0
+
qs@6.15.3:
dependencies:
es-define-property: 1.0.1
@@ -12719,6 +13082,22 @@ snapshots:
dependencies:
jsesc: 3.1.0
+ reka-ui@2.10.3(vue@3.5.41(typescript@6.0.3)):
+ dependencies:
+ '@floating-ui/dom': 1.8.0
+ '@floating-ui/vue': 1.1.11(vue@3.5.41(typescript@6.0.3))
+ '@internationalized/date': 3.12.3
+ '@internationalized/number': 3.6.7
+ '@tanstack/vue-virtual': 3.13.36(vue@3.5.41(typescript@6.0.3))
+ '@vueuse/core': 14.4.0(vue@3.5.41(typescript@6.0.3))
+ '@vueuse/shared': 14.4.0(vue@3.5.41(typescript@6.0.3))
+ aria-hidden: 1.2.6
+ defu: 6.1.7
+ ohash: 2.0.12
+ vue: 3.5.41(typescript@6.0.3)
+ transitivePeerDependencies:
+ - '@vue/composition-api'
+
reka-ui@2.10.4(vue@3.5.41(typescript@6.0.3)):
dependencies:
'@floating-ui/dom': 1.8.0
@@ -12919,37 +13298,37 @@ snapshots:
setprototypeof@1.2.0: {}
- sharp@0.35.3(@types/node@26.2.0):
+ sharp@0.35.4(@types/node@26.2.0):
dependencies:
'@img/colour': 1.1.0
detect-libc: 2.1.2
semver: 7.8.5
optionalDependencies:
- '@img/sharp-darwin-arm64': 0.35.3
- '@img/sharp-darwin-x64': 0.35.3
- '@img/sharp-freebsd-wasm32': 0.35.3
- '@img/sharp-libvips-darwin-arm64': 1.3.2
- '@img/sharp-libvips-darwin-x64': 1.3.2
- '@img/sharp-libvips-linux-arm': 1.3.2
- '@img/sharp-libvips-linux-arm64': 1.3.2
- '@img/sharp-libvips-linux-ppc64': 1.3.2
- '@img/sharp-libvips-linux-riscv64': 1.3.2
- '@img/sharp-libvips-linux-s390x': 1.3.2
- '@img/sharp-libvips-linux-x64': 1.3.2
- '@img/sharp-libvips-linuxmusl-arm64': 1.3.2
- '@img/sharp-libvips-linuxmusl-x64': 1.3.2
- '@img/sharp-linux-arm': 0.35.3
- '@img/sharp-linux-arm64': 0.35.3
- '@img/sharp-linux-ppc64': 0.35.3
- '@img/sharp-linux-riscv64': 0.35.3
- '@img/sharp-linux-s390x': 0.35.3
- '@img/sharp-linux-x64': 0.35.3
- '@img/sharp-linuxmusl-arm64': 0.35.3
- '@img/sharp-linuxmusl-x64': 0.35.3
- '@img/sharp-webcontainers-wasm32': 0.35.3
- '@img/sharp-win32-arm64': 0.35.3
- '@img/sharp-win32-ia32': 0.35.3
- '@img/sharp-win32-x64': 0.35.3
+ '@img/sharp-darwin-arm64': 0.35.4
+ '@img/sharp-darwin-x64': 0.35.4
+ '@img/sharp-freebsd-wasm32': 0.35.4
+ '@img/sharp-libvips-darwin-arm64': 1.3.3
+ '@img/sharp-libvips-darwin-x64': 1.3.3
+ '@img/sharp-libvips-linux-arm': 1.3.3
+ '@img/sharp-libvips-linux-arm64': 1.3.3
+ '@img/sharp-libvips-linux-ppc64': 1.3.3
+ '@img/sharp-libvips-linux-riscv64': 1.3.3
+ '@img/sharp-libvips-linux-s390x': 1.3.3
+ '@img/sharp-libvips-linux-x64': 1.3.3
+ '@img/sharp-libvips-linuxmusl-arm64': 1.3.3
+ '@img/sharp-libvips-linuxmusl-x64': 1.3.3
+ '@img/sharp-linux-arm': 0.35.4
+ '@img/sharp-linux-arm64': 0.35.4
+ '@img/sharp-linux-ppc64': 0.35.4
+ '@img/sharp-linux-riscv64': 0.35.4
+ '@img/sharp-linux-s390x': 0.35.4
+ '@img/sharp-linux-x64': 0.35.4
+ '@img/sharp-linuxmusl-arm64': 0.35.4
+ '@img/sharp-linuxmusl-x64': 0.35.4
+ '@img/sharp-webcontainers-wasm32': 0.35.4
+ '@img/sharp-win32-arm64': 0.35.4
+ '@img/sharp-win32-ia32': 0.35.4
+ '@img/sharp-win32-x64': 0.35.4
'@types/node': 26.2.0
optional: true
@@ -13512,7 +13891,7 @@ snapshots:
'@unrs/resolver-binding-win32-ia32-msvc': 1.12.2
'@unrs/resolver-binding-win32-x64-msvc': 1.12.2
- unstorage@1.17.5(@vercel/functions@3.9.5(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2)):
+ unstorage@1.17.5(@vercel/functions@3.9.7(ws@8.21.3))(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2)):
dependencies:
anymatch: 3.1.3
chokidar: 5.0.0
@@ -13523,7 +13902,7 @@ snapshots:
ofetch: 1.5.1
ufo: 1.6.4
optionalDependencies:
- '@vercel/functions': 3.9.5(ws@8.21.3)
+ '@vercel/functions': 3.9.7(ws@8.21.3)
db0: 0.3.4
ioredis: 5.11.1(supports-color@10.2.2)
@@ -13562,6 +13941,14 @@ snapshots:
vary@1.1.2: {}
+ vaul-vue@0.4.1(reka-ui@2.10.3(vue@3.5.41(typescript@6.0.3)))(vue@3.5.41(typescript@6.0.3)):
+ dependencies:
+ '@vueuse/core': 10.11.1(vue@3.5.41(typescript@6.0.3))
+ reka-ui: 2.10.3(vue@3.5.41(typescript@6.0.3))
+ vue: 3.5.41(typescript@6.0.3)
+ transitivePeerDependencies:
+ - '@vue/composition-api'
+
vaul-vue@0.4.1(reka-ui@2.10.4(vue@3.5.41(typescript@6.0.3)))(vue@3.5.41(typescript@6.0.3)):
dependencies:
'@vueuse/core': 10.11.1(vue@3.5.41(typescript@6.0.3))
@@ -13613,7 +14000,7 @@ snapshots:
- tsx
- yaml
- vite-plugin-checker@0.14.5(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(optionator@0.9.4)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3)):
+ vite-plugin-checker@0.14.5(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(optionator@0.9.4)(typescript@6.0.3)(vite@8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0))(vue-tsc@3.3.11(typescript@6.0.3)):
dependencies:
'@babel/code-frame': 7.29.7
chokidar: 5.0.0
@@ -13624,7 +14011,7 @@ snapshots:
tiny-invariant: 1.3.3
vite: 8.2.2(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)
optionalDependencies:
- eslint: 10.9.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.10.0(jiti@2.7.0)(supports-color@10.2.2)
optionator: 0.9.4
typescript: 6.0.3
vue-tsc: 3.3.11(typescript@6.0.3)
@@ -13718,10 +14105,10 @@ snapshots:
vue-devtools-stub@0.1.0: {}
- vue-eslint-parser@10.4.1(eslint@10.9.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2):
+ vue-eslint-parser@10.4.1(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2):
dependencies:
debug: 4.4.3(supports-color@10.2.2)
- eslint: 10.9.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.10.0(jiti@2.7.0)(supports-color@10.2.2)
eslint-scope: 9.1.2
eslint-visitor-keys: 5.0.1
espree: 11.2.0
@@ -13901,12 +14288,12 @@ snapshots:
compress-commons: 6.0.2
readable-stream: 4.7.0
- zod-to-json-schema@3.25.2(zod@4.4.3):
+ zod-to-json-schema@3.25.2(zod@4.6.1):
dependencies:
- zod: 4.4.3
+ zod: 4.6.1
zod@4.1.11: {}
- zod@4.4.3: {}
+ zod@4.6.1: {}
zwitch@2.0.4: {}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 3d1a108..6ca3246 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -10,7 +10,8 @@ blockExoticSubdeps: false
minimumReleaseAgeExclude:
# First-party packages, published and consumed here in the same session.
- - '@comark/*'
+ - '@comark/nuxt'
+ - '@comark/vue'
- comark
- comark-content
- nuxt-agent-discovery
diff --git a/server/api/code-explorer/[...path].get.ts b/server/api/code-explorer/[...path].get.ts
index 6edb4bb..1bd84ed 100644
--- a/server/api/code-explorer/[...path].get.ts
+++ b/server/api/code-explorer/[...path].get.ts
@@ -3,7 +3,7 @@ import { parseMarkdown, type MarkdownDocument } from 'comark'
import rangi from 'comark/plugins/rangi'
import fs from 'comark-content/sources/fs'
import github from 'comark-content/sources/github'
-import { geistTheme } from '../../../utils/geist-theme.ts'
+import { geistTheme } from '../../../utils/geist.ts'
// A read source for one example directory. Dev: working tree. Prod: authenticated GitHub — the repo may be
// private, so jsDelivr / unauthenticated raw are out. Mirrors {@link contentSource}'s dev/prod split.
diff --git a/server/api/content/[...path].get.ts b/server/api/content/[...path].get.ts
index e10db39..c4f39d7 100644
--- a/server/api/content/[...path].get.ts
+++ b/server/api/content/[...path].get.ts
@@ -1,6 +1,6 @@
/**
- * Single data endpoint: `content.handler()` dispatches `get`, `navigation`, `list` and custom handlers
- * (e.g. `search-sections`). Cached per-URL — see `routeRules`.
+ * Single data endpoint: `content.handler()` dispatches `get`, `navigation`, `list`, `manifest`
+ * and `snapshot`. Must be cached per-URL by layer consumer.
*/
export default defineEventHandler(async (event) => {
const content = await getProdContent()
diff --git a/server/api/content/blob/[sha]/[...path].get.ts b/server/api/content/blob/[sha]/[...path].get.ts
index a72ef7f..173c7ee 100644
--- a/server/api/content/blob/[sha]/[...path].get.ts
+++ b/server/api/content/blob/[sha]/[...path].get.ts
@@ -20,7 +20,6 @@ export default defineEventHandler(async (event) => {
// Also resolves short SHAs so one commit pins one content instance.
const fullSha = await authorizePreviewSha(sha)
- const content = await getPreviewContent(fullSha, `/api/content/blob/${sha}`)
-
- return await content.handler(toWebRequest(event))
+ // Head-of-branch requests reuse the shared prod instance; `servePreview()` handles that.
+ return servePreview(event, fullSha, `/blob/${rawSha}`)
})
diff --git a/server/api/content/head.get.ts b/server/api/content/head.get.ts
new file mode 100644
index 0000000..493c5bb
--- /dev/null
+++ b/server/api/content/head.get.ts
@@ -0,0 +1,10 @@
+/**
+ * The commit SHA production content is pinned to, or `null` in dev.
+ */
+export default defineEventHandler(async () => {
+ if (import.meta.dev) return { sha: null }
+
+ // Same resolution as the pages (`getProdContent`), so the search artifacts the client hydrates
+ // from can't come from a different commit than the rendered content — notably under a pin.
+ return { sha: await resolveProdSha() }
+})
diff --git a/server/api/content/pr/[number]/[...path].get.ts b/server/api/content/pr/[number]/[...path].get.ts
index 486a679..75de79e 100644
--- a/server/api/content/pr/[number]/[...path].get.ts
+++ b/server/api/content/pr/[number]/[...path].get.ts
@@ -1,6 +1,6 @@
/**
* Per-pull-request data endpoint: `/pr/:number` previews the PR's head commit. Follows new pushes
- * (the number → head SHA pointer lives in the short-TTL ref cache) and enforces the preview
+ * (the number → head SHA pointer lives in the 10-minute ref cache) and enforces the preview
* authorization: same-repo PRs always, fork PRs only with the `preview:enabled` label.
*/
export default defineEventHandler(async (event) => {
@@ -17,7 +17,5 @@ export default defineEventHandler(async (event) => {
}
const sha = await resolvePullPreviewSha(number)
- const content = await getPreviewContent(sha, `/api/content/pr/${number}`)
-
- return await content.handler(toWebRequest(event))
+ return servePreview(event, sha, `/pr/${rawNumber}`)
})
diff --git a/server/api/content/tree/[branch]/[...path].get.ts b/server/api/content/tree/[branch]/[...path].get.ts
index 362414c..0899003 100644
--- a/server/api/content/tree/[branch]/[...path].get.ts
+++ b/server/api/content/tree/[branch]/[...path].get.ts
@@ -14,7 +14,5 @@ export default defineEventHandler(async (event) => {
// `cacheMisses`: the ref comes from the URL, so a miss must not re-cost a GitHub call each time.
const sha = await resolveContentSha(branch, useRuntimeConfig(event).docs.contentDir, { cacheMisses: true })
- const content = await getPreviewContent(sha, `/api/content/tree/${encodeURIComponent(branch)}`)
-
- return await content.handler(toWebRequest(event))
+ return servePreview(event, sha, `/tree/${rawBranch}`)
})
diff --git a/server/api/revalidate.post.ts b/server/api/revalidate.post.ts
index 4a7451d..2c4f9c6 100644
--- a/server/api/revalidate.post.ts
+++ b/server/api/revalidate.post.ts
@@ -1,30 +1,26 @@
-import type { ContentListFile } from 'comark-content'
import { verify } from '@octokit/webhooks-methods'
+import { rawUrl } from '#agent-discovery'
+import { DEFAULT_CONTENT_NAME } from 'comark-content'
import { waitUntil } from '@vercel/functions'
/** Each re-render hits this same deployment, so the ceiling is about not stampeding ourselves. */
const REVALIDATE_CONCURRENCY = 8
-/** `Promise.allSettled` over `items`, at most `size` in flight. */
-async function settleInBatches(
- items: T[],
- size: number,
- fn: (item: T) => Promise
-): Promise[]> {
- const results: PromiseSettledResult[] = []
- for (let i = 0; i < items.length; i += size) {
- // Not `.map(fn)` — `map` passes the index, which lands in the callee's optional parameter.
- results.push(...(await Promise.allSettled(items.slice(i, i + size).map((item) => fn(item)))))
- }
- return results
-}
+/** Why a route was purged — one value per `addPath` call site below. */
+type PurgeReason = 'page' | 'payload' | 'raw' | 'nav' | 'global'
+
+/** Display/response order. */
+const REASON_ORDER: PurgeReason[] = ['page', 'payload', 'raw', 'nav', 'global']
+
+/** `nav` is the only unbounded reason (the whole site can be thousands of pages) — cap what the log prints. */
+const MAX_LOGGED_PATHS_PER_REASON = 5
export default defineEventHandler(async (event) => {
const { docs } = useRuntimeConfig(event)
const secret = docs.webhookSecret || process.env.WEBHOOK_SECRET
const bypassToken = docs.bypassToken || process.env.VERCEL_BYPASS_TOKEN
if (!secret || !bypassToken) {
- throw createError({ statusCode: 500, statusMessage: 'Webhook not configured' })
+ throw createError({ statusCode: 501, statusMessage: 'Revalidation webhook is not configured' })
}
const signature = getHeader(event, 'x-hub-signature-256')
@@ -41,182 +37,224 @@ export default defineEventHandler(async (event) => {
throw createError({ statusCode: 401, statusMessage: 'Invalid signature' })
}
+ const requestId = getHeader(event, 'x-vercel-id') ?? getHeader(event, 'x-request-id') ?? 'local'
+ const deliveryId = getHeader(event, 'x-github-delivery')
+ const tag = `[revalidate:${requestId}${deliveryId ? `:${deliveryId}` : ''}]`
+ const timings = createTimings()
+
+ const githubEvent = getHeader(event, 'x-github-event')
+ if (githubEvent !== 'push') {
+ console.log(`${tag} skipped: ${githubEvent ?? 'unknown'} event`)
+ return { ok: true, skipped: 'not-a-push-event', event: githubEvent }
+ }
+
const payload = JSON.parse(raw) as GitHubPushPayload
const branch = targetBranch()
const contentDir = docs.contentDir
const expectedRef = `refs/heads/${branch}`
- if (payload.ref !== expectedRef) {
- console.log(`[content] revalidate push skipped (ref=${payload.ref} !== expected=${expectedRef})`)
- return {
- ok: true,
- skipped: 'non-target-branch',
- expected: expectedRef,
- received: payload.ref,
- }
+ const repo = githubRepo()
+ if (payload.repository?.full_name && payload.repository.full_name !== repo) {
+ console.log(`${tag} skipped: repo=${payload.repository.full_name} !== expected=${repo}`)
+ return { ok: true, skipped: 'wrong-repo', expected: repo, received: payload.repository.full_name }
}
- // Classify changed content files. A file added in one commit and modified in
- // another counts as added; `.navigation.*` config files always touch navigation.
- const added = new Set()
- const removed = new Set()
- const modified = new Set()
- let navConfigTouched = false
- for (const commit of payload.commits ?? []) {
- for (const f of commit.added ?? []) {
- if (isContentMd(f)) added.add(f)
- else if (isNavConfig(f)) navConfigTouched = true
- }
- for (const f of commit.modified ?? []) {
- if (isContentMd(f)) modified.add(f)
- else if (isNavConfig(f)) navConfigTouched = true
- }
- for (const f of commit.removed ?? []) {
- if (isContentMd(f)) removed.add(f)
- else if (isNavConfig(f)) navConfigTouched = true
- }
+ if (payload.ref !== expectedRef) {
+ console.log(`${tag} skipped: ref=${payload.ref} !== expected=${expectedRef}`)
+ return { ok: true, skipped: 'non-target-branch', expected: expectedRef, received: payload.ref }
}
- for (const f of added) modified.delete(f)
- const changedFiles = [...added, ...modified, ...removed]
- if (changedFiles.length === 0 && !navConfigTouched) {
+ const changes = changesForPush(contentDir, payload.commits ?? [])
+ if (!changes.upserted.length && !changes.removed.length && !changes.navTouched) {
return { ok: true, skipped: 'no-content-changes' }
}
- const protocol = getRequestProtocol(event)
- const host = getRequestHost(event, { xForwardedHost: true })
- const baseURL = `${protocol}://${host}`
-
- // `x-vercel-protection-bypass` bypasses the SSO wall when the handler calls itself
- const readHeaders: Record = {}
- if (process.env.VERCEL_AUTOMATION_BYPASS_SECRET) {
- readHeaders['x-vercel-protection-bypass'] = process.env.VERCEL_AUTOMATION_BYPASS_SECRET
- }
-
- // `x-prerender-revalidate` purges the ISR cache
- const headers: Record = {
- ...readHeaders,
- 'x-prerender-revalidate': bypassToken,
- }
+ const buildId = useRuntimeConfig(event).app.buildId
+ const pathsToPurge = new Set()
+ const byReason = new Map>()
+ const rawPathFor = (path: string): string => new URL(rawUrl(event, path)).pathname
- const headSha = payload.head_commit?.id
- if (!headSha) {
- throw createError({ statusCode: 400, statusMessage: 'Missing head commit SHA' })
+ /** Add a path to the purge set, and track it by reason for the breakdown log. */
+ const addPath = (reason: PurgeReason, path: string): void => {
+ if (pathsToPurge.has(path)) return
+ pathsToPurge.add(path)
+ const paths = byReason.get(reason) ?? new Set()
+ paths.add(path)
+ byReason.set(reason, paths)
}
- // Bypass the short ref cache and write the canonical path-filtered revision before the purge fan-out,
- // so a freshly-purged page cannot re-render against a stale or payload-order-dependent content SHA.
- const contentSha = await resolveContentSha(branch, contentDir, { refresh: true })
-
- console.log(`[content] revalidate push headSha=${headSha} contentSha=${contentSha}`)
+ // Diffed against the live prod instance, already warm
+ const { headSha, newItems, pagePaths, navChanged } = await timings.time('rebuild', async () => {
+ const outdated = await getProdContent()
+ await outdated.init()
+ const oldItems = { ...(await outdated.manifest()).items }
- const requestId = getHeader(event, 'x-vercel-id') ?? getHeader(event, 'x-request-id') ?? 'local'
- const tag = `[revalidate:${requestId}]`
-
- // Planning before we respond costs two `init()` passes against GitHub's ~10s delivery timeout,
- // in exchange for diagnostics in the webhook body. Safe because everything here is idempotent,
- // so a retried delivery only repeats work. If it gets slow, move this into `waitUntil`.
- const beforeSha = payload.before
- let oldItems: Record = {}
- if (beforeSha && !/^0+$/.test(beforeSha)) {
- try {
- const oldContent = await createSourceContent(beforeSha)
- await oldContent.init()
- oldItems = (await oldContent.manifest()).items
- } catch (err) {
- const message = err instanceof Error ? err.message : err
- console.warn(`${tag} no before-manifest (${beforeSha}) — treating as full revalidate:`, message)
- }
- }
+ // Refresh the content SHA
+ const headSha = await resolveContentSha(branch, contentDir, { refresh: true })
- // The head snapshot has the same content directory as `contentSha`; populate the namespace that
- // production instances will read even when later commits in this push only changed code.
- const headContent = await createSourceContent(headSha, { cache: { driver: cacheDriver(contentSha) } })
- await headContent.init()
- const newItems = (await headContent.manifest()).items
+ // Throwaway instance: the diff needs the index only.
+ // It lands in the commit's cache namespace to be reused by the prod warm below.
+ const fresh = contentAt(headSha)
+ await fresh.init()
+ const newItems = (await fresh.manifest()).items
+ await fresh.dispose()
- const oldPaths = new Set(Object.keys(oldItems))
- const newPaths = Object.keys(newItems)
- const addedPaths = newPaths.filter((p) => !oldPaths.has(p))
- const removedPaths = [...oldPaths].filter((p) => !(p in newItems))
+ return { headSha, newItems, ...diffContent(changes, oldItems, newItems) }
+ })
- const metaChangedPaths: string[] = []
- for (const p of newPaths) {
- if (oldPaths.has(p) && hashManifestItem(oldItems[p]) !== hashManifestItem(newItems[p])) metaChangedPaths.push(p)
- }
- const navChanged = navConfigTouched || addedPaths.length > 0 || removedPaths.length > 0 || metaChangedPaths.length > 0
-
- // Payload routes are keyed by the build-id query on some deployments, so purge the exact
- // URL the browser loads (`…/_payload.json?`).
- const buildId = useRuntimeConfig(event).app.buildId
-
- // Any content change invalidates the llms indexes, the markdown sitemap, the feed, and the body-derived
- // search index.
- const paths = new Set(['/llms.txt', '/llms-full.txt', '/sitemap.md', '/rss.xml', '/api/content/search-sections'])
- for (const f of changedFiles) {
- const pageUrl = pageUrlForPath(f)
- if (pageUrl) {
- paths.add(payloadUrlForRoute(pageUrl, buildId))
- paths.add(pageUrl)
- }
- const rawUrl = rawUrlForPath(f)
- if (rawUrl) paths.add(rawUrl)
+ for (const path of pagePaths) {
+ addPath('page', path)
+ addPath('payload', payloadUrlForPage(path, buildId))
+ addPath('raw', rawPathFor(path))
}
// Navigation renders on every page, so a change to it re-renders all of them.
if (navChanged) {
for (const item of Object.values(newItems)) {
- if (item.meta.kind === 'document') {
- paths.add(item.path)
- paths.add(payloadUrlForRoute(item.path, buildId))
- }
+ if (item.meta.kind !== 'document') continue
+ addPath('nav', item.path)
+ addPath('nav', payloadUrlForPage(item.path, buildId))
+ addPath('nav', rawPathFor(item.path))
}
}
+ // Per-commit search artifacts (ISR, immutable).
+ const artifactBase = `/api/content/blob/${headSha}`
+ const pathsToWarm = [`${artifactBase}/manifest.json`, `${artifactBase}/snapshot/${DEFAULT_CONTENT_NAME}.json`]
+
+ // Any content change invalidates the global indexes: each is rebuilt from the whole tree.
+ for (const path of ['/llms.txt', '/llms-full.txt', '/rss.xml', '/sitemap.xml', '/sitemap.md']) {
+ addPath('global', path)
+ }
+
console.log(
`${tag} navChanged=${navChanged} ` +
- `(added=${addedPaths.length}, removed=${removedPaths.length}, meta=${metaChangedPaths.length}, navConfig=${navConfigTouched}) | ` +
- `files: +${added.size} ~${modified.size} -${removed.size} | ${paths.size} route(s)`
+ `(upserted=${changes.upserted.length}, removed=${changes.removed.length}, navConfig=${changes.navTouched}) | ` +
+ `${pathsToPurge.size} to purge, ${pathsToWarm.length} to warm | ${timings.format()}`
)
- if (metaChangedPaths.length) console.log(`${tag} meta changed: ${metaChangedPaths.join(', ')}`)
- if (addedPaths.length) console.log(`${tag} added: ${addedPaths.join(', ')}`)
- if (removedPaths.length) console.log(`${tag} removed: ${removedPaths.join(', ')}`)
+
+ logBreakdown(tag, byReason)
+ for (const path of pathsToWarm) console.log(`${tag} warm\t${path}`)
+
+ // Dev has no ISR cache to purge
+ if (import.meta.dev) {
+ return {
+ ok: true,
+ requestId,
+ deliveryId,
+ navChanged,
+ routes: routesBreakdown(byReason),
+ warm: pathsToWarm.length,
+ dev: true,
+ }
+ }
+
+ const protocol = getRequestProtocol(event)
+ const host = getRequestHost(event, { xForwardedHost: true })
+ const baseURL = `${protocol}://${host}`
+
+ // Lets the deployment call itself while Vercel Authentication is on (preview deploys).
+ const selfCall: Record = process.env.VERCEL_AUTOMATION_BYPASS_SECRET
+ ? { 'x-vercel-protection-bypass': process.env.VERCEL_AUTOMATION_BYPASS_SECRET }
+ : {}
+
+ // `x-prerender-revalidate` regenerates the ISR entry for the URL being fetched.
+ const purgeHeaders = { ...selfCall, 'x-prerender-revalidate': bypassToken }
// Vercel's native waitUntil, not Nitro's `event.waitUntil` — that one can orphan async work here.
waitUntil(
(async () => {
- const revalidate = (path: string, extra: Record = {}) =>
- $fetch(path, { baseURL, method: 'GET', headers: { ...headers, ...extra } }).catch((err) => {
- console.error(`${tag} ✗ ${path}`, err?.statusCode ?? err?.message ?? err)
- throw err
- })
-
- // Warm the per-SHA body cache so cold instances skip re-parsing from GitHub.
- // `metaOnly` became `partial` in comark-content 0.2.0 with no alias and consumers straddle both,
- // so send both keys — each version ignores the other's. Not inlined: as a literal,
- // excess-property checking rejects whichever key the installed types don't declare.
- const full = { partial: false, metaOnly: false }
- await headContent.init(full).catch((err) => {
- console.error(`${tag} cache warm failed`, err?.message ?? err)
- })
-
- await useStorage('cache:nuxt:payload').clear()
-
- // Bounded: a nav change queues two URLs per page, and every one re-enters this function.
- const results = await settleInBatches([...paths], REVALIDATE_CONCURRENCY, revalidate)
- const ok = results.filter((r) => r.status === 'fulfilled').length
- console.log(`${tag} complete: ${ok}/${results.length} succeeded`)
+ const absent: string[] = []
+
+ // The warm runs first:
+ // - ISR cache manifest and snapshot for the new SHA
+ // - Cache parsed items for the pages to purge and re-render
+ const warmResults = await timings.time('warm', () =>
+ settleInBatches(pathsToWarm, REVALIDATE_CONCURRENCY, (path) =>
+ $fetch(path, { baseURL, method: 'GET', headers: selfCall }).catch((error) => {
+ console.error(`${tag} ✗ ${path}`, error?.statusCode ?? error?.message ?? error)
+ throw error
+ })
+ )
+ )
+
+ const purgeResults = await timings.time('purge', () =>
+ settleInBatches([...pathsToPurge], REVALIDATE_CONCURRENCY, (path) =>
+ $fetch(path, { baseURL, method: 'GET', headers: purgeHeaders }).catch((error) => {
+ // Content with no page of its own (e.g. a partial) has nothing cached to purge.
+ if (error?.statusCode === 404) {
+ absent.push(path)
+ return
+ }
+ console.error(`${tag} ✗ ${path}`, error?.statusCode ?? error?.message ?? error)
+ throw error
+ })
+ )
+ )
+
+ const warmed = warmResults.filter((r) => r.status === 'fulfilled').length
+ const failed = [...warmResults, ...purgeResults].filter((r) => r.status === 'rejected').length
+ const purged = purgeResults.filter((r) => r.status === 'fulfilled').length - absent.length
+ console.log(
+ `${tag} complete: ${warmed} warmed, ${purged} purged, ${absent.length} absent, ` +
+ `${failed} failed | ${timings.format()} | total=${timings.since()}ms`
+ )
+ logAbsent(tag, absent)
})()
)
return {
ok: true,
requestId,
+ deliveryId,
navChanged,
- manifest: {
- added: addedPaths,
- removed: removedPaths,
- metaChanged: metaChangedPaths,
- },
+ manifest: { upserted: changes.upserted, removed: changes.removed },
+ routes: routesBreakdown(byReason),
}
})
+
+/** One log line per purged path, grouped by reason. */
+function logBreakdown(tag: string, byReason: Map>): void {
+ for (const reason of REASON_ORDER) {
+ const paths = byReason.get(reason)
+ if (!paths?.size) continue
+
+ const sorted = [...paths].sort()
+ for (const path of sorted.slice(0, MAX_LOGGED_PATHS_PER_REASON)) {
+ console.log(`${tag} ${reason}\t${path}`)
+ }
+ if (sorted.length > MAX_LOGGED_PATHS_PER_REASON) {
+ console.log(`${tag} ${reason}\t... (${sorted.length} total)`)
+ }
+ }
+}
+
+/** One log line per absent path — expected to be empty. */
+function logAbsent(tag: string, absent: string[]): void {
+ for (const path of [...absent].sort()) {
+ console.log(`${tag} absent\t${path}`)
+ }
+}
+
+/** Route counts by reason. */
+function routesBreakdown(byReason: Map>): { total: number } & Partial> {
+ const counts: Partial> = {}
+ for (const [reason, paths] of byReason) counts[reason] = paths.size
+
+ const total = Object.values(counts).reduce((sum, count) => sum + (count ?? 0), 0)
+ return { total, ...counts }
+}
+
+/** `Promise.allSettled` over `items`, at most `size` in flight. */
+async function settleInBatches(
+ items: T[],
+ size: number,
+ fn: (item: T) => Promise
+): Promise[]> {
+ const results: PromiseSettledResult[] = []
+ for (let i = 0; i < items.length; i += size) {
+ // Not `.map(fn)` — `map` passes the index, which lands in the callee's optional parameter.
+ results.push(...(await Promise.allSettled(items.slice(i, i + size).map((item) => fn(item)))))
+ }
+ return results
+}
diff --git a/server/utils/cache.ts b/server/utils/cache.ts
index 9c686e1..72f9dc6 100644
--- a/server/utils/cache.ts
+++ b/server/utils/cache.ts
@@ -5,52 +5,49 @@ import vercelRuntimeCache from 'unstorage/drivers/vercel-runtime-cache'
/** SHA-pinned content is immutable, so it can be cached for a long time. */
const TTL = 60 * 60 * 24
-/** Content refs move with their branches, so the pointer cache uses a short TTL. */
-const REF_TTL = 60
-
/** Whether the Vercel Runtime Cache is available (i.e. running on Vercel). */
function cacheAvailable(): boolean {
return !import.meta.dev && Boolean(process.env.VERCEL)
}
+/** Every namespace below degrades to per-process memory off Vercel if not available. */
+function runtimeCacheDriver(base: string, ttl?: number): Driver {
+ if (!cacheAvailable()) return memoryDriver()
+ return vercelRuntimeCache({ base, ttl })
+}
+
/**
* Bump when content parser/plugin configuration, relevant parser dependencies, or cached derived
* data changes. Keeping this explicit lets unrelated deployments reuse immutable content artifacts.
*/
export const CONTENT_PARSER_VERSION = 'v3'
-/** Per-parser-version, per-content-SHA driver backing comark's manifest and parsed bodies. */
-export function cacheDriver(sha: string): Driver {
- if (!cacheAvailable()) return memoryDriver()
- return vercelRuntimeCache({
- base: `content:${CONTENT_PARSER_VERSION}:${sha}`,
- ttl: TTL,
- })
+/**
+ * Comark cache: index, parsed bodies and artifacts of every commit.
+ * Sharing content across all perser versions BUT keys are per-sha.
+ */
+export function contentCacheDriver(): Driver {
+ return runtimeCacheDriver(`content:${CONTENT_PARSER_VERSION}`, TTL)
}
/**
- * Ad-hoc per-SHA storage for non-content data (commit history, RSS dates). Shares comark's
- * `cacheDriver(sha)` namespace rather than a separate unconfigured mount; `gh:...` keys can't
- * collide with comark's `:`.
+ * Shared driver backing branch pointers and preview authorization decisions (`github.ts`). The
+ * caller supplies a bounded default TTL, and individual preview entries can use a shorter TTL.
+ *
+ * TODO: Vercel Runtime Cache is **regional**, not global (https://vercel.com/docs/caching/runtime-cache):
+ * It assumes Functions run in a single region.
+ * Multi-region would confine the webhook's forced refresh to its region (others self-heal on TTL)
+ * We should reach for a globally replicated store (e.g. Edge Config).
*/
-export function shaCacheStorage(sha: string): Storage {
- return createStorage({ driver: cacheDriver(sha) })
+export function refCacheDriver(ttl: number): Driver {
+ // v2 prevents entries written before production refs gained a fallback TTL from surviving the migration.
+ return runtimeCacheDriver('content:refs:v2', ttl)
}
/**
- * Shared driver backing the branch + content directory → content commit pointer
- * (`resolveContentSha` in `github.ts`), in its own namespace so every instance reads one pointer
- * instead of keeping its own timer.
- *
- * Vercel Runtime Cache is **regional**, not global (https://vercel.com/docs/caching/runtime-cache):
- * this assumes Functions run in a single region (no `regions` in `vercel.json`/`nuxt.config.ts`).
- * Multi-region would confine the webhook's forced refresh to its region — others self-heal on TTL,
- * so reach for a globally replicated store (e.g. Edge Config) only if that day comes.
+ * Per-commit data Comark knows nothing about (commit history, RSS dates).
+ * Namespace is per-sha and keys start with `gh:`.
*/
-export function refCacheDriver(): Driver {
- if (!cacheAvailable()) return memoryDriver()
- return vercelRuntimeCache({
- base: 'content:refs',
- ttl: REF_TTL,
- })
+export function shaCacheStorage(sha: string): Storage {
+ return createStorage({ driver: runtimeCacheDriver(`content:${CONTENT_PARSER_VERSION}:${sha}`, TTL) })
}
diff --git a/server/utils/content.ts b/server/utils/content.ts
index 862de45..8c7455e 100644
--- a/server/utils/content.ts
+++ b/server/utils/content.ts
@@ -1,103 +1,64 @@
-import { defineContentPlugin, type CacheOptions, comarkContent } from 'comark-content';
+import { type ContentSource, DEFAULT_CONTENT_NAME } from 'comark-content'
import fs from 'comark-content/sources/fs'
import github from 'comark-content/sources/github'
-import rangi from 'comark/plugins/rangi'
-import security from 'comark/plugins/security'
-import emoji from 'comark/plugins/emoji'
-import toc from 'comark/plugins/toc'
-import mermaid from 'comark/plugins/mermaid'
-import yaml from 'comark-content/plugins/yaml'
-import tracingOtel from 'comark-content/plugins/tracing/otel'
-import { contentTracer } from './tracer.ts'
-import { geistTheme } from '../../utils/geist-theme.ts'
+import { withSnapshot } from 'comark-content/sources/snapshot'
+import { createRuntimeContentInstance } from '../../utils/content.ts'
/**
- * The instance this layer builds, derived from the factory rather than written
- * out.
- *
- * `ComarkContent` is the *unnarrowed* shape: its instance-name parameter drives
- * the conditional types behind `get()` and `list()`, so a concrete instance is
- * not assignable to it. Deriving instead of annotating keeps the narrowing that
- * `comark-content prepare` generates — `get('/known/path')` stays typed all the
- * way through the layer.
+ * The instance serving requests in this layer.
*/
-export type DocsContent = Awaited>
-
-// Rebuilt only when the head advances (see `getProdContent`). Holds the *promise*, not the instance: the
-// assignment lands after the await, so two requests on a cold instance would each build a CMS.
-let content: Promise | undefined
-
-// Bump CONTENT_PARSER_VERSION in `cache.ts` when these plugins or their options change cached output.
-const comarkPlugins = [
- mermaid({ theme: 'zinc-light', themeDark: 'zinc-dark' }),
- rangi({ theme: geistTheme }),
- toc({ depth: 3 }),
- emoji(),
- security({
- blockedTags: ['script', 'iframe', 'embed', 'form', 'base', 'meta', 'link', 'style'],
- allowDataImages: false,
- }),
-]
-
-// Bound to THIS instance so a preview content instance serves its own version's sections, not production's.
-const searchSectionsPlugin = defineContentPlugin(() => ({
- name: 'search-sections',
- setup(ctx) {
- ctx.addServeHandler('search-sections', async () => Response.json(await buildSearchSections(ctx as unknown as DocsContent)))
- },
-}))()
+export type DocsContent = ReturnType
/**
- * Create a new content instance reading content at `ref` (a commit SHA or branch). `remote` forces the
- * GitHub source, `cache` overrides comark's (in-memory by default), `watch` is dev file watching.
+ * Holds the source, the plugins and the cache driver.
+ * Once per function invocation.
+ * Base for all instances "cloned" with `withRef(sha)` in `contentAt()`.
*/
-export async function createSourceContent(
- ref: string,
- opts: { remote?: boolean; cache?: CacheOptions; basePath?: string; watch?: boolean } = {}
-) {
- // A no-op unless the consumer shadows it from their own `server/utils/`. Re-typed as the layer's own
- // options: a consumer's hook is declared against the wide `ContentOptions`, and letting that widen the
- // argument would erase the source and plugin types `comarkContent` infers from the literal.
- const tracer = contentTracer()
- const instance = comarkContent({
- markdown: {
- plugins: comarkPlugins,
- },
- source: contentSource(ref, { remote: opts.remote }),
- plugins: [
- yaml(), // enable .navigation.yml to be detected
- searchSectionsPlugin,
- tracer && tracingOtel({ tracer }),
- ],
- cache: opts.cache,
- basePath: opts.basePath,
- })
+let base: DocsContent | undefined
- // Only the default instance watches: others read a fixed ref that can't change, and retaining
- // `watch()`'s stop function to release the watcher would leak once the preview entry is evicted.
- if (import.meta.dev && opts.watch) {
- await instance.watch()
- instance.hooks.hook('watch:file:update', (_source:string, key: string) => {
- invalidateSearchSections(instance)
- console.log(`${key} updated`)
- })
- instance.hooks.hook('watch:file:remove', () => invalidateSearchSections(instance))
- }
+function getBaseContent(): DocsContent {
+ base ??= createBaseContent()
+ return base
+}
- return instance
+function createBaseContent() {
+ return createRuntimeContentInstance({
+ source: contentSource(),
+ cache: { driver: contentCacheDriver() },
+ })
+}
+
+/**
+ * Base instance cloned and pinned to a SHA.
+ * Nothing is read until the first call.
+ * `dispose()` it when you replace it.
+*/
+export function contentAt(sha: string): DocsContent {
+ return getBaseContent().withRef(sha)
}
-// The content commit this instance is pinned to. Pinning GitHub reads to an immutable SHA rather
-// than the branch name bypasses the stale `raw.githubusercontent.com/` CDN.
-let headRef: string | undefined
+// The content commit currently served, once `getProdContent()` has resolved one.
+// Pin GitHub reads to an immutable SHA:
+// bypasses the stale `raw.githubusercontent.com/` CDN.
+let headSha: string | undefined
+/**
+ * The pinned head commit, or nothing while none is resolved (dev, off-Vercel, pre-first-resolve).
+ */
+export function getHeadSha(): string | undefined {
+ return headSha
+}
+
+/**
+ * The pinned head SHA, falling back to the branch while none is resolved.
+ * The fallback only ever applies off-Vercel (self-hosted, `nuxt preview`, `vercel dev`).
+ */
export function getHeadRef(): string {
- headRef ??= targetBranch()
- return headRef
+ return headSha ?? targetBranch()
}
/**
- * The SHA production should currently serve:
+ * The SHA prod instance currently serves:
* - global config pin if one is set (production only)
* - latest commit touching the content directory via `resolveContentSha()`
*/
@@ -110,88 +71,125 @@ export async function resolveProdSha(): Promise {
return resolveContentSha(targetBranch(), contentDir)
}
+// Rebuild the promise when the head advances.
+// Holds the promise to ensure two requests on a cold process don't each build one.
+let prod: Promise | undefined
+
/**
- * Shared content instance for the lifetime of this server instance, pinned to `headRef`. In production every
- * call resolves the current head via `resolveProdSha()` — a shared, short-TTL cache, not a per-instance
- * timer — and rebuilds when that advances. Previews stay pinned.
+ * Shared instance for the lifetime of the process, pinned to `headSha`.
+ * Always resolves the head via `resolveProdSha()`.
+ * Swaps to a new pinned instance when the head advances.
*/
export async function getProdContent(): Promise {
if (['production', 'preview'].includes(process.env.VERCEL_ENV || '')) {
const sha = await resolveProdSha()
- if (sha !== getHeadRef()) {
- console.log(`[content] head ${getHeadRef()} -> ${sha}`)
- headRef = sha
- content = undefined // the old instance baked its source at the old commit
+ if (sha !== headSha) {
+ if (headSha) {
+ console.log(`[comark-docs] New head: ${headSha} -> ${sha}`)
+ void prod?.then((instance) => instance.dispose()).catch(() => {})
+ prod = undefined
+ }
+ headSha = sha
}
}
- if (!content) {
- content = createSourceContent(getHeadRef(), {
- watch: true,
- cache: {
- driver: cacheDriver(getHeadRef()),
- },
- }).catch((error) => {
- // Don't memoize a failed build — the next request should retry.
- content = undefined
+ if (!prod) {
+ prod = (async () => {
+ const instance = import.meta.dev ? await watchedDevContent() : contentAt(getHeadRef())
+ const startedAt = performance.now()
+ await instance.init()
+ recordDuration('content.init.ms', startedAt)
+ return instance
+ })().catch((error) => {
+ prod = undefined
throw error
})
}
- return content
+ return prod
}
-function contentSource(ref: string, opts: { remote?: boolean } = {}) {
+/** The unpinned base in development — it reads the working tree and follows file changes. */
+async function watchedDevContent(): Promise {
+ const instance = getBaseContent()
+ await instance.watch()
+ instance.hooks.hook('watch:file:update', (_source: string, key: string) => {
+ invalidateSearchSections(instance)
+ console.log(`[comark-docs] ${key} updated`)
+ })
+ instance.hooks.hook('watch:file:remove', () => invalidateSearchSections(instance))
+ return instance
+}
+
+/**
+ * The source every instance derives from.
+ * `withRef(sha)` pins it to a commit.
+ *
+ * Production:
+ * - GitHub reads the tree and files at `sha`
+ * - the build snapshot supplies every body whose source hash is unchanged
+ *
+ * Development:
+ * - unpinned reads the working tree, which `watch()` follows
+ * - pinned reads the repo at that commit
+ * - no snapshot
+ */
+function contentSource(): ContentSource {
const { docs } = useRuntimeConfig()
if (import.meta.dev) {
- if (opts.remote) return gitLocalSource(ref, docs.contentDir)
-
- return fs(docs.contentPath)
+ return {
+ ...fs(docs.contentPath),
+ withRef: (ref) => gitLocalSource(ref, docs.contentDir),
+ }
}
- return github({
+ const source = github({
repo: githubRepo(),
- branch: ref,
+ branch: targetBranch(),
path: docs.contentDir,
token: githubToken(),
- // `ref` is an immutable commit SHA => we can cache hard.
+ // Reads happen through `withRef()`, an immutable commit => cache hard.
ttl: 60 * 60 * 24,
})
+
+ // Snaphot build during build time by `modules/snapshot/` is used.
+ // Snpahost is pinned to the latest commit at the time of the build.
+ // First head moves, only the bodies whose source hash matches are reused.
+ return withSnapshot(source, () => readSnapshot(), () => readManifest())
}
-/** Per-instance registry of preview CMS instances, keyed by `::`. */
-const contentPreviewInstances = new Map>()
-
-// Bound required: each entry is a content instance with its own manifest and parsed bodies, and public
-// `/tree/:branch` / `/blob/:sha` let a crawler mint one per SHA. Evicted refs just rebuild, their
-// bodies surviving in the per-SHA Runtime Cache.
-const MAX_PREVIEW_INSTANCES = 8
-
-export function getPreviewContent(sha: string, basePath: string): Promise {
- const key = `${basePath}::${sha}`
- const existing = contentPreviewInstances.get(key)
- if (existing) {
- // `Map` preserves insertion order, which is the whole LRU: re-insert so the MRU key is last.
- contentPreviewInstances.delete(key)
- contentPreviewInstances.set(key, existing)
- return existing
+/**
+ * Read the build-time snapshot, or nothing when this deployment did not ship one.
+ */
+async function readSnapshot(): Promise {
+ const span = contentTracer()?.startSpan('snapshot:read')
+ const startedAt = performance.now()
+ try {
+ // Untyped read: unstorage runs every value through `destr`, so this arrives already parsed.
+ const data = await useStorage('assets:comark-content').get(`${DEFAULT_CONTENT_NAME}/snapshot.json`)
+ const hit = data != null
+ span?.setAttribute('comark.snapshot.hit', hit)
+ recordDuration('content.snapshot.read.ms', startedAt, { hit: String(hit) })
+ return data
+ } finally {
+ span?.end()
}
+}
- const instance = createSourceContent(sha, {
- remote: true,
- basePath,
- cache: { driver: cacheDriver(sha) },
- }).catch((error) => {
- contentPreviewInstances.delete(key)
- throw error
- })
- contentPreviewInstances.set(key, instance)
-
- while (contentPreviewInstances.size > MAX_PREVIEW_INSTANCES) {
- const oldest = contentPreviewInstances.keys().next()
- if (oldest.done) break
- contentPreviewInstances.delete(oldest.value)
+/**
+ * Read the build-time snapshot, or nothing when this deployment did not ship one.
+ */
+async function readManifest(): Promise {
+ const span = contentTracer()?.startSpan('manifest:read')
+ const startedAt = performance.now()
+ try {
+ // Untyped read: unstorage runs every value through `destr`, so this arrives already parsed.
+ const data = await useStorage('assets:comark-content').get(`${DEFAULT_CONTENT_NAME}/manifest.json`)
+ const hit = data != null
+ span?.setAttribute('comark.manifest.hit', hit)
+ recordDuration('content.manifest.read.ms', startedAt, { hit: String(hit) })
+ return data
+ } finally {
+ span?.end()
}
-
- return instance
}
diff --git a/server/utils/github.ts b/server/utils/github.ts
index eb995a5..c47bdbc 100644
--- a/server/utils/github.ts
+++ b/server/utils/github.ts
@@ -1,5 +1,6 @@
import { createHash, timingSafeEqual } from 'node:crypto'
import { createStorage } from 'unstorage'
+import { fetchLastContentCommit } from '../../utils/github'
export interface GitHubCommit {
added?: string[]
@@ -13,6 +14,7 @@ export interface GitHubPushPayload {
before?: string
commits?: GitHubCommit[]
head_commit?: GitHubCommit & { id?: string }
+ repository?: { full_name?: string }
}
/** Constant-time string comparison. */
@@ -44,13 +46,25 @@ export function targetBranch(): string {
return process.env.VERCEL_GIT_COMMIT_REF || useRuntimeConfig().docs.github.branch || 'main'
}
-// Branch + content directory → content commit SHA pointer, shared across every instance so only one
-// pays for the GitHub API call per TTL window. See `refCacheDriver()` for the single-region assumption.
-const refStorage = createStorage({ driver: refCacheDriver() })
+/** The webhook refreshes production immediately; this bounds recovery when delivery fails. */
+const PRODUCTION_REF_TTL = 60 * 60
+
+/** Moving previews and negative decisions refresh or recover more frequently. */
+const PREVIEW_REF_TTL = 600
+
+// Branch + content directory → content commit SHA pointer, shared across every instance. The
+// production branch is refreshed by its push webhook; TTL remains a bounded fallback.
+const refStorage = createStorage({ driver: refCacheDriver(PRODUCTION_REF_TTL) })
const normalizeContentDir = (contentDir: string) => contentDir.replace(/^\/+|\/+$/g, '')
const refKey = (branch: string, contentDir: string) =>
`branch:${encodeURIComponent(branch)}:path:${encodeURIComponent(normalizeContentDir(contentDir))}`
+/** Production has a longer fallback because its webhook owns the normal refresh path. */
+function refTtl(branch: string): number {
+ if (process.env.VERCEL_ENV === 'production' && branch === targetBranch()) return PRODUCTION_REF_TTL
+ return PREVIEW_REF_TTL
+}
+
/** Sentinel for "this ref doesn't resolve" — see the negative caching in `resolveContentSha`. */
const UNRESOLVED = '\0unresolved'
@@ -79,38 +93,33 @@ export async function resolveContentSha(
if (cached) return cached
}
- const token = githubToken()
- let commits: Array<{ sha: string }>
+ // Shared with the build-time snapshot, which walks the built commit instead of a branch — see
+ // `fetchLastContentCommit()`. One query, so the two cannot drift apart.
+ let sha: string | undefined
try {
- commits = await $fetch>(`https://api.github.com/repos/${githubRepo()}/commits`, {
- headers: {
- Accept: 'application/vnd.github+json',
- ...(token ? { Authorization: `Bearer ${token}` } : {}),
- },
- query: {
- sha: branch,
- path: normalizeContentDir(contentDir),
- per_page: 1,
- },
+ sha = await fetchLastContentCommit({
+ repo: githubRepo(),
+ path: contentDir,
+ ref: branch,
+ token: githubToken(),
})
} catch (error: unknown) {
// Only a definitive 404 is cacheable; a 5xx, rate-limit 403 or network blip stays retryable.
const failure = error as { statusCode?: number; response?: { status?: number } }
const status = failure.statusCode ?? failure.response?.status
if (status === 404) {
- if (opts.cacheMisses) await refStorage.setItem(key, UNRESOLVED)
+ if (opts.cacheMisses) await refStorage.setItem(key, UNRESOLVED, { ttl: PREVIEW_REF_TTL })
throw createError({ statusCode: 404, statusMessage: `Ref not found: ${branch}` })
}
throw error
}
- const sha = commits[0]?.sha
if (!sha) {
- if (opts.cacheMisses) await refStorage.setItem(key, UNRESOLVED)
+ if (opts.cacheMisses) await refStorage.setItem(key, UNRESOLVED, { ttl: PREVIEW_REF_TTL })
throw createError({ statusCode: 404, statusMessage: `Content not found at ref: ${branch}` })
}
- await refStorage.setItem(key, sha)
+ await refStorage.setItem(key, sha, { ttl: refTtl(branch) })
return sha
}
@@ -158,8 +167,8 @@ function pullAllowsPreview(pull: GitHubPullSummary): boolean {
* 1. an associated PR allows it (same-repo PR, or a fork PR carrying `preview:enabled`), or
* 2. the commit is in the production branch's history (version-history links).
*
- * Decisions live in the short-TTL ref cache — positive ones too, so removing the label revokes
- * access within a TTL. Skipped in dev, where refs resolve against the local checkout instead.
+ * Decisions live in the preview ref cache — positive ones too, so removing the label revokes
+ * access within 10 minutes. Skipped in dev, where refs resolve against the local checkout instead.
*/
export async function authorizePreviewSha(sha: string): Promise {
if (import.meta.dev) return sha
@@ -172,7 +181,7 @@ export async function authorizePreviewSha(sha: string): Promise {
if (cached) return cached
const deny = async (): Promise => {
- await refStorage.setItem(key, UNRESOLVED)
+ await refStorage.setItem(key, UNRESOLVED, { ttl: PREVIEW_REF_TTL })
throw createError({ statusCode: 404, statusMessage: `No preview available for commit: ${sha}` })
}
@@ -211,7 +220,7 @@ export async function authorizePreviewSha(sha: string): Promise {
if (!allowed) return deny()
- await refStorage.setItem(key, fullSha)
+ await refStorage.setItem(key, fullSha, { ttl: PREVIEW_REF_TTL })
return fullSha
}
@@ -219,8 +228,8 @@ export async function authorizePreviewSha(sha: string): Promise {
* Authorize a `/pr/:number` preview and resolve it to the PR's head commit SHA.
*
* Same rule as `authorizePreviewSha`: same-repo PRs are always previewable, fork PRs only with the
- * `preview:enabled` label. Cached in the short-TTL ref cache so the preview follows new pushes and
- * label removal revokes it within a TTL.
+ * `preview:enabled` label. Cached for 10 minutes so the preview follows new pushes and label removal
+ * revokes it within the same bound.
*/
export async function resolvePullPreviewSha(number: number): Promise {
const key = `preview:pr:${number}`
@@ -231,7 +240,7 @@ export async function resolvePullPreviewSha(number: number): Promise {
if (cached) return cached
const deny = async (): Promise => {
- await refStorage.setItem(key, UNRESOLVED)
+ await refStorage.setItem(key, UNRESOLVED, { ttl: PREVIEW_REF_TTL })
throw createError({ statusCode: 404, statusMessage: `No preview available for PR #${number}` })
}
@@ -248,7 +257,7 @@ export async function resolvePullPreviewSha(number: number): Promise {
const sha = pull.head?.sha
if (!sha || !pullAllowsPreview(pull)) return deny()
- await refStorage.setItem(key, sha)
+ await refStorage.setItem(key, sha, { ttl: PREVIEW_REF_TTL })
return sha
}
diff --git a/server/utils/local.ts b/server/utils/local.ts
index 8d7ae1b..5e92c9b 100644
--- a/server/utils/local.ts
+++ b/server/utils/local.ts
@@ -4,7 +4,7 @@ import type { Source } from 'comark-content'
import type { PageCommit } from './github'
const exec = promisify(execFile)
-/** Root of the git repository holding the content (resolved at build time by modules/config.ts). */
+/** Root of the git repository holding the content (resolved at build time by modules/config/). */
function repoRoot(): string {
return useRuntimeConfig().docs.repoRoot
}
diff --git a/server/utils/metrics.ts b/server/utils/metrics.ts
new file mode 100644
index 0000000..ba656d1
--- /dev/null
+++ b/server/utils/metrics.ts
@@ -0,0 +1,13 @@
+import { metric } from '@vercel/functions'
+
+/**
+ * Report an elapsed time to Vercel Observability, and hand it back for logging.
+ *
+ * `metric()` talks to the runtime over an IPC global that only exists on Vercel, so this is a
+ * no-op locally rather than an error.
+ */
+export function recordDuration(name: string, startedAt: number, tags?: Record): number {
+ const ms = Math.round(performance.now() - startedAt)
+ metric(name, ms, tags)
+ return ms
+}
diff --git a/server/utils/paths.ts b/server/utils/paths.ts
index ae8f710..4988fa2 100644
--- a/server/utils/paths.ts
+++ b/server/utils/paths.ts
@@ -1,55 +1,4 @@
-/** Repo-relative content prefix (e.g. `docs/content/`), derived at build time by modules/config.ts. */
+/** Repo-relative content prefix (e.g. `docs/content/`), derived at build time by modules/config/. */
export function contentPrefix(): string {
return `${useRuntimeConfig().docs.contentDir.replace(/\/$/, '')}/`
}
-
-/** Whether a GitHub repo path is a content markdown file. */
-export function isContentMd(path: string): boolean {
- return path.startsWith(contentPrefix()) && path.toLowerCase().endsWith('.md')
-}
-
-/** Whether a GitHub repo path is a navigation config file (`.navigation.yml` / `.json`). */
-export function isNavConfig(path: string): boolean {
- return path.startsWith(contentPrefix()) && /\.navigation\.(?:ya?ml|json)$/i.test(path)
-}
-
-/**
- * Parse a content repo path into route segments (`1.getting-started/2.intro.md` →
- * `['getting-started', 'intro']`). `isIndex` covers both `index.md` and `index/index.md`.
- */
-export function slugFromPath(path: string): { isIndex: boolean; segments: string[] } | null {
- const prefix = contentPrefix()
- if (!path.startsWith(prefix) || !path.toLowerCase().endsWith('.md')) return null
-
- const relative = path.slice(prefix.length, -3)
- const segments = relative.split('/').map((s) => s.replace(/^\d+\./, ''))
- const last = segments[segments.length - 1]
- const isIndex = last === 'index'
- if (isIndex) segments.pop()
- return { isIndex, segments }
-}
-
-/** Frontend page route (e.g. `1.getting-started/2.intro.md` → `/getting-started/intro`, root → `/`). */
-export function pageUrlForPath(path: string): string | null {
- const result = slugFromPath(path)
- if (!result) return null
- const { isIndex, segments } = result
- if (isIndex && segments.length === 0) return '/'
- return `/${segments.join('/')}`
-}
-
-/** Raw markdown route — the only per-file route that stays cached, as `/api/pages` is served live. */
-export function rawUrlForPath(path: string): string | null {
- const result = slugFromPath(path)
- if (!result) return null
-
- const { isIndex, segments } = result
- if (isIndex && segments.length === 0) return '/raw/index.md'
- return `/raw/${segments.join('/')}.md`
-}
-
-/** Nuxt payload route for a frontend page route */
-export function payloadUrlForRoute(route: string, buildId?: string): string {
- const path = `${route === '/' ? '' : route}/_payload.json`
- return buildId ? `${path}?${buildId}` : path
-}
diff --git a/server/utils/preview.ts b/server/utils/preview.ts
new file mode 100644
index 0000000..97e9cf9
--- /dev/null
+++ b/server/utils/preview.ts
@@ -0,0 +1,45 @@
+import type { DocsContent } from './content'
+
+// Preview instances for `/blob/:sha`, `/tree/:branch` and `/pr/:number`.
+const previews = new Map()
+
+const MAX_PREVIEW_INSTANCES = 8
+
+function getPreviewContent(sha: string): DocsContent {
+ const existing = previews.get(sha)
+ if (existing) {
+ previews.delete(sha)
+ previews.set(sha, existing)
+ return existing
+ }
+
+ const instance = contentAt(sha)
+ previews.set(sha, instance)
+
+ while (previews.size > MAX_PREVIEW_INSTANCES) {
+ const oldest = previews.keys().next()
+ if (oldest.done) break
+ const evicted = previews.get(oldest.value)
+ previews.delete(oldest.value)
+ void evicted?.dispose().catch(() => {})
+ }
+
+ return instance
+}
+
+/**
+ * Serve a preview request through the instance pinned to `sha`.
+ */
+export async function servePreview(event: Parameters[0], sha: string, segment: string) {
+ const request = toWebRequest(event)
+ const url = new URL(request.url)
+ url.pathname = url.pathname.replace(segment, '')
+ const rewritten = new Request(url, request)
+
+ if (!getHeadSha() || sha === getHeadSha()) {
+ const instance = await getProdContent()
+ // Recheck after promise resolves.
+ if (sha === getHeadSha()) return instance.handler(rewritten)
+ }
+ return getPreviewContent(sha).handler(rewritten)
+}
diff --git a/server/utils/timing.ts b/server/utils/timing.ts
new file mode 100644
index 0000000..17a4cce
--- /dev/null
+++ b/server/utils/timing.ts
@@ -0,0 +1,29 @@
+/** Named phase timings for one revalidate webhook run. */
+export interface Timings {
+ /** Time a sync or async `fn` under `label`; records its duration and returns its result. */
+ time(label: string, fn: () => T | Promise): Promise
+ /** `label=123ms label2=45ms`, in recorded order — for one log line. */
+ format(): string
+ /** ms since this recorder was created — spans the sync response and the background `waitUntil` phase. */
+ since(): number
+}
+
+export function createTimings(): Timings {
+ const start = performance.now()
+ const entries: { label: string; ms: number }[] = []
+
+ async function time(label: string, fn: () => T | Promise): Promise {
+ const phaseStart = performance.now()
+ try {
+ return await fn()
+ } finally {
+ entries.push({ label, ms: Math.round(performance.now() - phaseStart) })
+ }
+ }
+
+ function format(): string {
+ return entries.map(({ label, ms }) => `${label}=${ms}ms`).join(' ')
+ }
+
+ return { time, format, since: () => Math.round(performance.now() - start) }
+}
diff --git a/server/utils/webhook.ts b/server/utils/webhook.ts
new file mode 100644
index 0000000..d08b8fb
--- /dev/null
+++ b/server/utils/webhook.ts
@@ -0,0 +1,117 @@
+import { DEFAULT_CONTENT_NAME, type ContentListFile } from 'comark-content'
+import type { GitHubCommit } from './github'
+import { hashManifestItem } from './json'
+
+/** How a push changed the content source, already filtered to `contentDir`. */
+export interface ContentChanges {
+ /** Manifest keys (`default/`) of files added or modified. */
+ upserted: string[]
+ /** Manifest keys of files removed — only the previous manifest can resolve their paths. */
+ removed: string[]
+ /** A `.navigation.*` file changed, so the tree changed regardless of which pages did. */
+ navTouched: boolean
+}
+
+/** Files the content source can actually serve — matches the parsers installed in `content.ts`. */
+const CONTENT_EXTENSIONS = ['.md', '.yml', '.yaml', '.json']
+
+/** The content instance's name (see `createBaseContent()` in `content.ts`) — unnamed, so `default`. */
+const SOURCE_NAME = DEFAULT_CONTENT_NAME
+
+/**
+ * A push's changed content files, named by their manifest key (`default/`) — the
+ * reverse of `meta.key`, so a diff against `manifest.items` doesn't need to re-derive file → URL
+ * mappings that comark already owns.
+ */
+export function changesForPush(contentDir: string, commits: GitHubCommit[]): ContentChanges {
+ const upserted = new Set()
+ const removed = new Set()
+ let navTouched = false
+
+ const consider = (file: string, into: Set) => {
+ const key = manifestKeyFor(file, contentDir)
+ if (!key) return
+
+ if (isNavConfigFile(file)) navTouched = true
+ else into.add(key)
+ }
+
+ for (const commit of commits) {
+ for (const file of commit.added ?? []) consider(file, upserted)
+ for (const file of commit.modified ?? []) consider(file, upserted)
+ for (const file of commit.removed ?? []) consider(file, removed)
+ }
+
+ // A path removed and re-added in the same push is an upsert, not a removal.
+ for (const key of upserted) removed.delete(key)
+
+ return { upserted: [...upserted], removed: [...removed], navTouched }
+}
+
+/** Repo-relative path → its key in the manifest, or `null` when it can't be a content file. */
+function manifestKeyFor(file: string, contentDir: string): string | null {
+ const dir = contentDir.replace(/^\/+|\/+$/g, '')
+ const prefix = dir ? `${dir}/` : ''
+
+ if (prefix && !file.startsWith(prefix)) return null
+ if (!CONTENT_EXTENSIONS.some((ext) => file.toLowerCase().endsWith(ext))) return null
+
+ return `${SOURCE_NAME}/${file.slice(prefix.length)}`
+}
+
+/** Directory configuration (`.navigation.yml`), which contributes to the tree rather than a page. */
+function isNavConfigFile(file: string): boolean {
+ return /\.navigation\.(?:ya?ml|json)$/i.test(file)
+}
+
+/**
+ * The payload URL a client-side navigation fetches for `path`
+ */
+export function payloadUrlForPage(path: string, buildId?: string): string {
+ const base = path === '/' ? '/_payload.json' : `${path.replace(/\/$/, '')}/_payload.json`
+ return buildId ? `${base}?_b=${buildId}` : base
+}
+
+/** `default/` (a manifest key) → page path, the reverse of what the path-keyed manifest gives. */
+export function indexByFileKey(items: Record): Map {
+ const index = new Map()
+ for (const item of Object.values(items)) index.set(item.meta.key, item.path)
+ return index
+}
+
+/**
+ * Which pages a push changed, and whether the tree itself moved.
+ */
+export function diffContent(
+ changes: ContentChanges,
+ before: Record,
+ after: Record
+): { pagePaths: string[]; navChanged: boolean } {
+ const pagePaths = new Set()
+
+ const afterByKey = indexByFileKey(after)
+ const beforeByKey = indexByFileKey(before)
+
+ for (const key of changes.upserted) {
+ const path = afterByKey.get(key)
+ if (path) pagePaths.add(path)
+ }
+ for (const key of changes.removed) {
+ const path = beforeByKey.get(key)
+ if (path) pagePaths.add(path)
+ }
+
+ const beforeKeys = Object.keys(before)
+ const afterKeys = Object.keys(after)
+ const navChanged =
+ beforeKeys.length !== afterKeys.length ||
+ afterKeys.some((key) => !before[key]) ||
+ // Listing fields (title, description, icon, `navigation`…) are what the tree renders from.
+ afterKeys.some((key) => before[key] && !sameListing(before[key]!, after[key]!))
+
+ return { pagePaths: [...pagePaths], navChanged }
+}
+
+function sameListing(a: ContentListFile, b: ContentListFile): boolean {
+ return a.path === b.path && hashManifestItem(a) === hashManifestItem(b)
+}
diff --git a/test/content-contract.test.ts b/test/content-contract.test.ts
index 20de79d..8825ba9 100644
--- a/test/content-contract.test.ts
+++ b/test/content-contract.test.ts
@@ -5,12 +5,20 @@
* which is how comark-content#77's `metaOnly` -> `partial` rename silently degraded
* the webhook's body warm-up. This exercises the surface the layer depends on.
*/
+import { mkdtemp, readFile } from 'node:fs/promises'
+import { tmpdir } from 'node:os'
+import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { describe, expect, it } from 'vitest'
-import { comarkContent, defineContentPlugin } from 'comark-content'
+import { comarkContent, DEFAULT_CONTENT_NAME, readArtifact } from 'comark-content'
+import { writeSnapshots } from 'comark-content/build'
import fsSource from 'comark-content/sources/fs'
import githubSource from 'comark-content/sources/github'
-import { createContentClient, defineContentClientPlugin } from 'comark-content/client'
+import snapshot, { withSnapshot } from 'comark-content/sources/snapshot'
+import { createContentClient } from 'comark-content/client'
+import { comarkContent as runtimeComarkContent, DEFAULT_CONTENT_NAME as RUNTIME_CONTENT_NAME, readArtifact as runtimeReadArtifact } from 'comark-content/runtime'
+import sqliteWasm from 'comark-content/database/sqlite-wasm'
+import sqliteFullTextSearch from 'comark-content/plugins/sqlite-full-text-search'
import memoryDriver from 'unstorage/drivers/memory'
const fixture = fileURLToPath(new URL('./fixtures/content-contract', import.meta.url))
@@ -36,7 +44,18 @@ function createFixtureContent() {
describe('comark-content contract', () => {
it('exposes every entrypoint the layer imports', () => {
- for (const entry of [comarkContent, defineContentPlugin, fsSource, githubSource, createContentClient, defineContentClientPlugin]) {
+ for (const entry of [
+ comarkContent,
+ readArtifact,
+ fsSource,
+ githubSource,
+ createContentClient,
+ runtimeComarkContent,
+ runtimeReadArtifact,
+ // Browser-only at runtime, but the subpaths resolve under node — enough to catch a rename.
+ sqliteWasm,
+ sqliteFullTextSearch,
+ ]) {
expect(typeof entry).toBe('function')
}
})
@@ -71,24 +90,130 @@ describe('comark-content contract', () => {
expect(cached!.nodes.length).toBeGreaterThan(0)
})
- it('dispatches plugin serve handlers through content.handler', async () => {
- // Mirrors the `search-sections` plugin in server/utils/content.ts.
- const ping = defineContentPlugin(() => ({
- name: 'ping',
- setup(ctx) {
- ctx.addServeHandler('ping', async () => Response.json({ ok: true }))
- },
- }))
-
- const content = comarkContent({
- source: fsSource(fixture),
- cache: { driver: memoryDriver() },
- plugins: [ping()],
+ it('serves the manifest and snapshot artifacts through content.handler', async () => {
+ const content = createFixtureContent()
+ await content.init(full)
+
+ // The exact paths the search worker fetches and `modules/config/` declares ISR rules for.
+ for (const path of ['manifest.json', `snapshot/${DEFAULT_CONTENT_NAME}.json`]) {
+ const response = await content.handler(new Request(`http://localhost/api/content/${path}`))
+ expect(response.status, path).toBe(200)
+ const artifact = await response.json()
+ expect(Object.keys(artifact), path).toContain('checksum')
+ expect(Object.keys(await readArtifact(artifact)).length, path).toBeGreaterThan(0)
+ }
+ })
+
+ it('hydrates a sourceless instance from those artifacts', async () => {
+ const server = createFixtureContent()
+ await server.init(full)
+
+ const fetchArtifact = async (path: string) =>
+ await (await server.handler(new Request(`http://localhost/api/content/${path}`))).json()
+
+ // The search feature is this round-trip, so a break here is a silently empty search index.
+ // `snapshot()`'s first argument is the full-body tier; the second (optional) manifest tier
+ // lets a bare `init()` skip downloading bodies until a document is actually requested.
+ const client = comarkContent({
+ source: snapshot(
+ () => fetchArtifact(`snapshot/${DEFAULT_CONTENT_NAME}.json`),
+ () => fetchArtifact('manifest.json')
+ ),
})
+ await client.init()
- const response = await content.handler(new Request('http://localhost/api/content/ping'))
+ expect(Object.keys((await client.manifest()).items)).toEqual(['/'])
- expect(response.status).toBe(200)
- expect(await response.json()).toEqual({ ok: true })
+ // Bodies have to arrive parsed: the client has no source to read a document from.
+ const doc = await client.get('/')
+ expect(doc?.data?.title).toBe('Contract fixture')
+ expect(doc?.nodes?.length).toBeGreaterThan(0)
+ })
+
+ it('hydrates a runtime-entry instance from those artifacts', async () => {
+ // `app/workers/search.ts` imports from `comark-content/runtime`, the parser-free entry.
+ // A snapshot-only instance must still hydrate there, and the name has to stay in step with
+ // the artifact paths the worker fetches.
+ expect(RUNTIME_CONTENT_NAME).toBe(DEFAULT_CONTENT_NAME)
+
+ const server = createFixtureContent()
+ await server.init(full)
+
+ const fetchArtifact = async (path: string) =>
+ await (await server.handler(new Request(`http://localhost/api/content/${path}`))).json()
+
+ const client = runtimeComarkContent({
+ source: snapshot(
+ () => fetchArtifact(`snapshot/${RUNTIME_CONTENT_NAME}.json`),
+ () => fetchArtifact('manifest.json')
+ ),
+ })
+ await client.init()
+
+ expect(Object.keys((await client.manifest()).items)).toEqual(['/'])
+ const doc = await client.get('/')
+ expect(doc?.data?.title).toBe('Contract fixture')
+ expect(doc?.nodes?.length).toBeGreaterThan(0)
+ })
+
+ describe('build-time snapshot', () => {
+ /**
+ * `modules/snapshot/` writes it with `writeSnapshots()`, and
+ * `server/utils/content.ts` reads it back through a Nitro server asset. Three things here are
+ * layout, not behaviour, and all are silent when wrong: the per-instance subdirectory, the fact
+ * that a server asset hands back JSON *text*, and `manifest: false` suppressing the light tier
+ * the layer does not read.
+ */
+ async function writeSnapshotFile() {
+ const dir = await mkdtemp(join(tmpdir(), 'comark-snapshot-'))
+ await writeSnapshots(createFixtureContent(), { dir, manifest: false })
+ // One directory per instance, named after it — ours is unnamed, so `default`.
+ const read = (file: string) => readFile(join(dir, DEFAULT_CONTENT_NAME, file), 'utf8')
+ return { snapshot: () => read('snapshot.json'), manifest: () => read('manifest.json') }
+ }
+
+ it('writes the snapshot alone when the manifest tier is off', async () => {
+ const stored = await writeSnapshotFile()
+
+ await expect(stored.snapshot()).resolves.toContain('Contract fixture')
+ await expect(stored.manifest()).rejects.toThrow()
+ })
+
+ it('hydrates a withSnapshot instance from the snapshot without reading the source', async () => {
+ const stored = await writeSnapshotFile()
+
+ // A source that throws on any read: hydrating from the snapshot must not touch it. This is the
+ // cold start being bought — in production the reads it stands in for are GitHub API calls.
+ const unreachable = {
+ ...fsSource(fixture),
+ keys: () => {
+ throw new Error('the origin was walked')
+ },
+ }
+
+ const content = comarkContent({
+ source: withSnapshot(unreachable, stored.snapshot),
+ cache: { driver: memoryDriver() },
+ })
+ await content.init(full)
+
+ expect(Object.keys((await content.manifest()).items)).toEqual(['/'])
+ const doc = await content.get('/')
+ expect(doc?.data?.title).toBe('Contract fixture')
+ expect(doc?.nodes?.length).toBeGreaterThan(0)
+ })
+
+ it('falls back to the source when no snapshot is stored', async () => {
+ // What every ref other than the build commit gets: loaders return `null`, so the origin is
+ // the only provider. A snapshot that cannot prove it belongs to this ref must never be used.
+ const content = comarkContent({
+ source: withSnapshot(fsSource(fixture), () => null),
+ cache: { driver: memoryDriver() },
+ })
+ await content.init(full)
+
+ expect(Object.keys((await content.manifest()).items)).toEqual(['/'])
+ expect((await content.get('/'))?.data?.title).toBe('Contract fixture')
+ })
})
})
diff --git a/test/geist-theme.test.ts b/test/geist.test.ts
similarity index 99%
rename from test/geist-theme.test.ts
rename to test/geist.test.ts
index a24a6e6..bcb48ab 100644
--- a/test/geist-theme.test.ts
+++ b/test/geist.test.ts
@@ -1,7 +1,7 @@
import { describe, expect, it } from 'vitest'
import { parseMarkdown } from 'comark'
import rangi from 'comark/plugins/rangi'
-import { geistDark, geistLight, geistTheme } from '../utils/geist-theme'
+import { geistDark, geistLight, geistTheme } from '../utils/geist'
describe('Geist syntax theme', () => {
it('uses the live Geist light syntax roles', () => {
diff --git a/test/git.test.ts b/test/git.test.ts
index 54f9356..7728da7 100644
--- a/test/git.test.ts
+++ b/test/git.test.ts
@@ -1,6 +1,9 @@
+import { execFileSync } from 'node:child_process'
+import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'
+import { tmpdir } from 'node:os'
+import { dirname, join } from 'node:path'
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
-import { parseGitRemote } from '../utils/git'
-import { inferSiteURL } from '../utils/meta'
+import { getLastCommit, getTreeSha, hasParent, parseGitRemote } from '../utils/git'
describe('parseGitRemote', () => {
it('parses SSH remotes', () => {
@@ -31,53 +34,76 @@ describe('parseGitRemote', () => {
})
})
-describe('inferSiteURL', () => {
- const keys = [
- 'NUXT_PUBLIC_SITE_URL',
- 'NUXT_SITE_URL',
- 'VERCEL_PROJECT_PRODUCTION_URL',
- 'VERCEL_BRANCH_URL',
- 'VERCEL_URL',
- 'URL',
- 'CI_PAGES_URL',
- 'CF_PAGES_URL',
- ]
- let saved: Record
-
- // `Reflect.deleteProperty` rather than `delete process.env[key]`: same effect,
- // without tripping `no-dynamic-delete`.
- const unset = (key: string) => Reflect.deleteProperty(process.env, key)
-
- beforeEach(() => {
- saved = Object.fromEntries(keys.map((key) => [key, process.env[key]]))
- for (const key of keys) unset(key)
+
+describe('commit and tree helpers', () => {
+ let repo: string
+
+ const run = (...args: string[]) => execFileSync('git', args, { cwd: repo, stdio: 'ignore' })
+ const write = async (file: string, body: string) => {
+ await mkdir(dirname(join(repo, file)), { recursive: true })
+ await writeFile(join(repo, file), body, 'utf8')
+ }
+
+ beforeEach(async () => {
+ repo = await mkdtemp(join(tmpdir(), 'comark-git-'))
+ run('init', '-q', '-b', 'main')
+ run('config', 'user.email', 'test@example.com')
+ run('config', 'user.name', 'Test')
+
+ await write('content/index.md', '# one\n')
+ run('add', '-A')
+ run('commit', '-qm', 'add content')
+
+ // A later commit that leaves `content/` untouched, so HEAD is not the last content commit.
+ await write('src/app.ts', 'export const a = 1\n')
+ run('add', '-A')
+ run('commit', '-qm', 'add code')
})
- afterEach(() => {
- for (const [key, value] of Object.entries(saved)) {
- if (value === undefined) unset(key)
- else process.env[key] = value
- }
+ afterEach(async () => {
+ await rm(repo, { recursive: true, force: true })
})
- it('returns undefined when nothing is set', () => {
- expect(inferSiteURL()).toBeUndefined()
+ it('finds the last commit touching a directory, not HEAD', () => {
+ const head = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: repo, encoding: 'utf8' }).trim()
+ const last = getLastCommit(repo, 'content')
+
+ expect(last).toMatch(/^[0-9a-f]{40}$/)
+ expect(last).not.toBe(head)
})
- it('adds https to a bare Vercel host', () => {
- process.env.VERCEL_URL = 'my-app-abc123.vercel.app'
- expect(inferSiteURL()).toBe('https://my-app-abc123.vercel.app')
+ it('returns the same tree for a ref whose content matches HEAD', () => {
+ // The whole safety property of the build-time snapshot: the commit it is labelled with has to hold
+ // the content that was parsed. Here the code commit did not touch `content/`, so both agree.
+ const last = getLastCommit(repo, 'content')!
+ expect(getTreeSha(repo, last, 'content')).toBe(getTreeSha(repo, 'HEAD', 'content'))
})
- it('prefers the explicit override over the platform value', () => {
- process.env.VERCEL_URL = 'my-app-abc123.vercel.app'
- process.env.NUXT_PUBLIC_SITE_URL = 'https://docs.example.com'
- expect(inferSiteURL()).toBe('https://docs.example.com')
+ it('returns a different tree once the content changes', async () => {
+ const before = getTreeSha(repo, 'HEAD', 'content')!
+
+ await write('content/index.md', '# two\n')
+ run('add', '-A')
+ run('commit', '-qm', 'edit content')
+
+ expect(getTreeSha(repo, 'HEAD', 'content')).not.toBe(before)
+ // A stale label is what the snapshot must never be written under.
+ expect(getTreeSha(repo, 'HEAD~1', 'content')).toBe(before)
})
- it('prefers the production URL over the per-branch one', () => {
- process.env.VERCEL_BRANCH_URL = 'branch.vercel.app'
- process.env.VERCEL_PROJECT_PRODUCTION_URL = 'docs.comark.dev'
- expect(inferSiteURL()).toBe('https://docs.comark.dev')
+ it('returns undefined for a ref or path outside the checkout', () => {
+ expect(getTreeSha(repo, 'HEAD', 'nope')).toBeUndefined()
+ expect(getTreeSha(repo, 'a'.repeat(40), 'content')).toBeUndefined()
+ expect(getLastCommit(repo, 'nope')).toBeUndefined()
+ })
+
+ it('reports a missing parent at the root commit', () => {
+ const root = execFileSync('git', ['rev-list', '--max-parents=0', 'HEAD'], {
+ cwd: repo,
+ encoding: 'utf8',
+ }).trim()
+
+ expect(hasParent(repo, 'HEAD')).toBe(true)
+ expect(hasParent(repo, root)).toBe(false)
})
})
diff --git a/test/github.test.ts b/test/github.test.ts
index b39103d..24c4904 100644
--- a/test/github.test.ts
+++ b/test/github.test.ts
@@ -1,6 +1,9 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { resolveContentSha } from '../server/utils/github'
+/** One commits-query response. `resolveContentSha` reads only `sha`. */
+const commits = (sha: string) => new Response(JSON.stringify([{ sha }]), { status: 200 })
+
afterEach(() => {
vi.unstubAllEnvs()
vi.unstubAllGlobals()
@@ -8,21 +11,23 @@ afterEach(() => {
describe('resolveContentSha', () => {
it('resolves the latest commit touching the configured content directory', async () => {
- const fetch = vi.fn().mockResolvedValue([{ sha: 'content-sha' }])
- vi.stubGlobal('$fetch', fetch)
+ const fetch = vi.fn().mockResolvedValue(commits('content-sha'))
+ vi.stubGlobal('fetch', fetch)
await expect(resolveContentSha('feat/docs', '/docs/content/')).resolves.toBe('content-sha')
- expect(fetch).toHaveBeenCalledWith(
- 'https://api.github.com/repos/comarkdown/comark-docs/commits',
- expect.objectContaining({
- query: { sha: 'feat/docs', path: 'docs/content', per_page: 1 },
- })
- )
+
+ const requested = new URL(String(fetch.mock.calls[0]![0]))
+ expect(requested.pathname).toBe('/repos/comarkdown/comark-docs/commits')
+ expect(Object.fromEntries(requested.searchParams)).toEqual({
+ sha: 'feat/docs',
+ path: 'docs/content',
+ per_page: '1',
+ })
})
it('caches each branch and content directory independently', async () => {
- const fetch = vi.fn().mockResolvedValueOnce([{ sha: 'docs-sha' }]).mockResolvedValueOnce([{ sha: 'api-sha' }])
- vi.stubGlobal('$fetch', fetch)
+ const fetch = vi.fn().mockResolvedValueOnce(commits('docs-sha')).mockResolvedValueOnce(commits('api-sha'))
+ vi.stubGlobal('fetch', fetch)
await expect(resolveContentSha('test/cache-key', 'docs/content')).resolves.toBe('docs-sha')
await expect(resolveContentSha('test/cache-key', 'docs/content')).resolves.toBe('docs-sha')
@@ -31,8 +36,8 @@ describe('resolveContentSha', () => {
})
it('can refresh a cached content revision for the push webhook', async () => {
- const fetch = vi.fn().mockResolvedValueOnce([{ sha: 'before' }]).mockResolvedValueOnce([{ sha: 'after' }])
- vi.stubGlobal('$fetch', fetch)
+ const fetch = vi.fn().mockResolvedValueOnce(commits('before')).mockResolvedValueOnce(commits('after'))
+ vi.stubGlobal('fetch', fetch)
await expect(resolveContentSha('test/refresh', 'docs/content')).resolves.toBe('before')
await expect(resolveContentSha('test/refresh', 'docs/content')).resolves.toBe('before')
diff --git a/test/paths.test.ts b/test/paths.test.ts
index e8c96bb..36f36e9 100644
--- a/test/paths.test.ts
+++ b/test/paths.test.ts
@@ -1,14 +1,6 @@
import { afterEach, describe, expect, it } from 'vitest'
import { resetRuntimeConfig, setRuntimeConfig } from './setup'
-import {
- contentPrefix,
- isContentMd,
- isNavConfig,
- pageUrlForPath,
- payloadUrlForRoute,
- rawUrlForPath,
- slugFromPath,
-} from '../server/utils/paths'
+import { contentPrefix } from '../server/utils/paths'
afterEach(resetRuntimeConfig)
@@ -19,79 +11,3 @@ describe('contentPrefix', () => {
expect(contentPrefix()).toBe('docs/content/')
})
})
-
-describe('isContentMd', () => {
- it('matches markdown under the content dir only', () => {
- expect(isContentMd('content/index.md')).toBe(true)
- expect(isContentMd('content/1.guide/2.intro.MD')).toBe(true)
- expect(isContentMd('content/.navigation.yml')).toBe(false)
- expect(isContentMd('README.md')).toBe(false)
- expect(isContentMd('other/content/x.md')).toBe(false)
- })
-
- it('follows a nested content dir', () => {
- setRuntimeConfig({ contentDir: 'docs/content' })
- expect(isContentMd('docs/content/x.md')).toBe(true)
- expect(isContentMd('content/x.md')).toBe(false)
- })
-})
-
-describe('isNavConfig', () => {
- it('matches the yml/yaml/json navigation files', () => {
- expect(isNavConfig('content/1.guide/.navigation.yml')).toBe(true)
- expect(isNavConfig('content/.navigation.yaml')).toBe(true)
- expect(isNavConfig('content/.navigation.json')).toBe(true)
- expect(isNavConfig('content/navigation.yml')).toBe(false)
- expect(isNavConfig('content/x.md')).toBe(false)
- })
-})
-
-describe('slugFromPath', () => {
- it('strips numeric ordering prefixes at every level', () => {
- expect(slugFromPath('content/1.getting-started/2.intro.md')).toEqual({
- isIndex: false,
- segments: ['getting-started', 'intro'],
- })
- })
-
- it('treats index files as their parent', () => {
- expect(slugFromPath('content/index.md')).toEqual({ isIndex: true, segments: [] })
- expect(slugFromPath('content/1.guide/index.md')).toEqual({ isIndex: true, segments: ['guide'] })
- })
-
- it('returns null for anything outside the content dir', () => {
- expect(slugFromPath('README.md')).toBeNull()
- expect(slugFromPath('content/.navigation.yml')).toBeNull()
- })
-})
-
-describe('pageUrlForPath', () => {
- it('maps content files to page routes', () => {
- expect(pageUrlForPath('content/index.md')).toBe('/')
- expect(pageUrlForPath('content/1.guide/index.md')).toBe('/guide')
- expect(pageUrlForPath('content/1.guide/2.intro.md')).toBe('/guide/intro')
- expect(pageUrlForPath('content/x.yml')).toBeNull()
- })
-})
-
-describe('rawUrlForPath', () => {
- it('maps content files to their raw markdown mirror', () => {
- expect(rawUrlForPath('content/index.md')).toBe('/raw/index.md')
- expect(rawUrlForPath('content/1.guide/2.intro.md')).toBe('/raw/guide/intro.md')
- expect(rawUrlForPath('content/1.guide/index.md')).toBe('/raw/guide.md')
- expect(rawUrlForPath('README.md')).toBeNull()
- })
-})
-
-describe('payloadUrlForRoute', () => {
- it('builds the payload URL the browser actually requests', () => {
- expect(payloadUrlForRoute('/')).toBe('/_payload.json')
- expect(payloadUrlForRoute('/guide/intro')).toBe('/guide/intro/_payload.json')
- })
-
- it('appends the build id when there is one', () => {
- // The webhook has to purge the exact keyed URL, not the bare path.
- expect(payloadUrlForRoute('/guide', 'abc123')).toBe('/guide/_payload.json?abc123')
- expect(payloadUrlForRoute('/', 'abc123')).toBe('/_payload.json?abc123')
- })
-})
diff --git a/test/setup.ts b/test/setup.ts
index ba5873c..d5a4cde 100644
--- a/test/setup.ts
+++ b/test/setup.ts
@@ -1,7 +1,7 @@
// Nitro auto-imports, provided by hand: modules under `server/` are written against Nitro's globals, so
// importing one directly in a test leaves those names undefined. Declaring the few the tests touch here beats
// pulling in the whole Nuxt/Nitro harness for a handful of pure functions. `useRuntimeConfig` returns the shape
-// `modules/config.ts` seeds.
+// `modules/config/` seeds.
import memoryDriver from 'unstorage/drivers/memory'
export interface TestRuntimeConfig {
diff --git a/test/webhook.test.ts b/test/webhook.test.ts
new file mode 100644
index 0000000..25cc85f
--- /dev/null
+++ b/test/webhook.test.ts
@@ -0,0 +1,112 @@
+import { describe, expect, it } from 'vitest'
+import type { ContentListFile } from 'comark-content'
+import type { GitHubCommit } from '../server/utils/github'
+import { changesForPush, diffContent, indexByFileKey, payloadUrlForPage } from '../server/utils/webhook'
+
+const commit = (partial: GitHubCommit): GitHubCommit => partial
+
+describe('changesForPush', () => {
+ it('classifies added/modified/removed content files, keyed by their manifest key', () => {
+ const commits = [
+ commit({
+ added: ['content/1.guide/2.intro.md'],
+ modified: ['content/index.md'],
+ removed: ['content/old.md'],
+ }),
+ ]
+ expect(changesForPush('content', commits)).toEqual({
+ upserted: ['default/1.guide/2.intro.md', 'default/index.md'],
+ removed: ['default/old.md'],
+ navTouched: false,
+ })
+ })
+
+ it('ignores files outside the content dir', () => {
+ expect(changesForPush('content', [commit({ modified: ['README.md', 'other/content/x.md'] })])).toEqual({
+ upserted: [],
+ removed: [],
+ navTouched: false,
+ })
+ })
+
+ it('follows a nested content dir', () => {
+ expect(changesForPush('docs/content', [commit({ modified: ['docs/content/x.md'] })])).toEqual({
+ upserted: ['default/x.md'],
+ removed: [],
+ navTouched: false,
+ })
+ })
+
+ it('covers every parser extension, not just markdown', () => {
+ const commits = [commit({ added: ['content/data.yml', 'content/data.yaml', 'content/data.json'] })]
+ expect(changesForPush('content', commits).upserted).toEqual([
+ 'default/data.yml',
+ 'default/data.yaml',
+ 'default/data.json',
+ ])
+ })
+
+ it('flags a navigation config file instead of collecting it', () => {
+ const commits = [commit({ modified: ['content/1.guide/.navigation.yml'] })]
+ expect(changesForPush('content', commits)).toEqual({ upserted: [], removed: [], navTouched: true })
+ })
+
+ it('treats a path removed and re-added in the same push as an upsert', () => {
+ const commits = [commit({ added: ['content/index.md'], removed: ['content/index.md'] })]
+ expect(changesForPush('content', commits)).toEqual({
+ upserted: ['default/index.md'],
+ removed: [],
+ navTouched: false,
+ })
+ })
+})
+
+describe('payloadUrlForPage', () => {
+ it('matches the `_b` query param Nuxt requests (`nuxt/dist/app/composables/payload.js`)', () => {
+ expect(payloadUrlForPage('/')).toBe('/_payload.json')
+ expect(payloadUrlForPage('/guide/intro')).toBe('/guide/intro/_payload.json')
+ expect(payloadUrlForPage('/guide', 'abc123')).toBe('/guide/_payload.json?_b=abc123')
+ expect(payloadUrlForPage('/', 'abc123')).toBe('/_payload.json?_b=abc123')
+ })
+})
+
+describe('indexByFileKey', () => {
+ it('maps a manifest key back to its page path', () => {
+ const items: Record = {
+ '/guide/intro': { path: '/guide/intro', data: {}, meta: { key: 'content/1.guide/2.intro.md' } } as never,
+ }
+ expect(indexByFileKey(items).get('content/1.guide/2.intro.md')).toBe('/guide/intro')
+ })
+})
+
+describe('diffContent', () => {
+ const file = (path: string, key: string, data: Record = {}): ContentListFile =>
+ ({ path, data, meta: { key } }) as never
+
+ it('resolves upserted/removed manifest keys to page paths', () => {
+ const before = { '/old': file('/old', 'content/old.md') }
+ const after = { '/guide/intro': file('/guide/intro', 'content/1.guide/2.intro.md') }
+ const changes = { upserted: ['content/1.guide/2.intro.md'], removed: ['content/old.md'], navTouched: false }
+ expect(diffContent(changes, before, after).pagePaths.sort()).toEqual(['/guide/intro', '/old'])
+ })
+
+ it('flags navChanged when a page is added or removed', () => {
+ const before = { '/a': file('/a', 'content/a.md') }
+ const after = { '/a': file('/a', 'content/a.md'), '/b': file('/b', 'content/b.md') }
+ expect(diffContent({ upserted: [], removed: [], navTouched: false }, before, after).navChanged).toBe(true)
+ })
+
+ it('flags navChanged when listing data changes, even with the same page set', () => {
+ const before = { '/a': file('/a', 'content/a.md', { title: 'A' }) }
+ const after = { '/a': file('/a', 'content/a.md', { title: 'B' }) }
+ expect(diffContent({ upserted: [], removed: [], navTouched: false }, before, after).navChanged).toBe(true)
+ })
+
+ it('does not flag navChanged when nothing listing-relevant moved', () => {
+ const before = { '/a': file('/a', 'content/a.md', { title: 'A' }) }
+ const after = { '/a': file('/a', 'content/a.md', { title: 'A' }) }
+ expect(diffContent({ upserted: ['content/a.md'], removed: [], navTouched: false }, before, after).navChanged).toBe(
+ false
+ )
+ })
+})
diff --git a/utils/content.ts b/utils/content.ts
new file mode 100644
index 0000000..120ac52
--- /dev/null
+++ b/utils/content.ts
@@ -0,0 +1,60 @@
+import type { Tracer } from '@opentelemetry/api'
+import { type ContentOptions, comarkContent } from 'comark-content'
+import markdown from 'comark-content/plugins/markdown'
+import yaml from 'comark-content/plugins/yaml'
+import tracingOtel from 'comark-content/plugins/tracing/otel'
+import rangi from 'comark/plugins/rangi'
+import security from 'comark/plugins/security'
+import emoji from 'comark/plugins/emoji'
+import toc from 'comark/plugins/toc'
+import mermaid from 'comark/plugins/mermaid'
+import { geistTheme } from './geist.ts'
+import { contentTracer } from '../server/utils/tracer.ts'
+
+/** Frontmatter kept in the manifest, so `list()` and `navigation()` render without reading bodies. */
+const LISTING_FIELDS = ['title', 'description', 'navigation', 'icon', 'layout']
+
+// Bump CONTENT_PARSER_VERSION in `server/utils/cache.ts` when these plugins or their options change cached output.
+const comarkPlugins = [
+ mermaid({ theme: 'zinc-light', themeDark: 'zinc-dark' }),
+ rangi({ theme: geistTheme }),
+ toc({ depth: 3 }),
+ emoji(),
+ security({
+ blockedTags: ['script', 'iframe', 'embed', 'form', 'base', 'meta', 'link', 'style'],
+ allowDataImages: false,
+ }),
+]
+
+/**
+ * The parser, in one place for:
+ * - The build-time snapshot
+ * - The runtime instance
+ */
+function create(options: Pick, tracer?: Tracer) {
+ return comarkContent({
+ source: options.source,
+ plugins: [
+ markdown({
+ comark: { plugins: comarkPlugins },
+ listingFields: LISTING_FIELDS,
+ }),
+ yaml({ listingFields: LISTING_FIELDS }),
+ tracer && tracingOtel({ tracer }),
+ ],
+ cache: options.cache,
+ basePath: options.basePath,
+ })
+}
+
+/** An instance serving requests: traced, and cached per content SHA. */
+export function createRuntimeContentInstance(options: Pick) {
+ return create(options, contentTracer())
+}
+
+/**
+ * The throwaway instance the build-time snapshot is parsed with (`modules/snapshot/`).
+ */
+export function createBuildContentInstance(options: Pick) {
+ return create(options)
+}
diff --git a/utils/geist-theme.ts b/utils/geist.ts
similarity index 100%
rename from utils/geist-theme.ts
rename to utils/geist.ts
diff --git a/utils/git.ts b/utils/git.ts
index ad1a7ba..e323fa6 100644
--- a/utils/git.ts
+++ b/utils/git.ts
@@ -1,4 +1,4 @@
-import { execSync } from 'node:child_process'
+import { execFileSync } from 'node:child_process'
export interface GitInfo {
name: string
@@ -6,11 +6,10 @@ export interface GitInfo {
url: string
}
-function git(command: string, cwd: string): string | undefined {
+/** Run git with an argv array — no shell, so paths with spaces need no quoting. */
+function git(args: string[], cwd: string): string | undefined {
try {
- return execSync(`git ${command}`, { cwd, stdio: ['ignore', 'pipe', 'ignore'] })
- .toString()
- .trim()
+ return execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
} catch {
return undefined
}
@@ -27,19 +26,19 @@ export function getGitBranch(cwd: string): string {
if (envName && envName !== 'HEAD') return envName
- const branch = git('rev-parse --abbrev-ref HEAD', cwd)
+ const branch = git(['rev-parse', '--abbrev-ref', 'HEAD'], cwd)
return branch && branch !== 'HEAD' ? branch : 'main'
}
/** Absolute path of the git repository root containing `cwd`, if any. */
export function getGitRoot(cwd: string): string | undefined {
- return git('rev-parse --show-toplevel', cwd)
+ return git(['rev-parse', '--show-toplevel'], cwd)
}
/**
* Owner/name/url from a git remote URL, in both forms `git remote get-url` emits (`git@host:owner/name.git`,
* `https://host/owner/name(.git)`). Split out from `getLocalGitInfo` so the regex is testable without a
- * checkout — every inferred default in `modules/config.ts` (site name, edit links, webhook repo) flows from it.
+ * checkout — every inferred default in `modules/config/` (site name, edit links, webhook repo) flows from it.
*/
export function parseGitRemote(remote: string): GitInfo | undefined {
const match = remote.trim().match(/^(?:git@|https?:\/\/)([^/:]+)[/:]([^/]+)\/(.+?)(?:\.git)?$/)
@@ -51,7 +50,7 @@ export function parseGitRemote(remote: string): GitInfo | undefined {
/** Owner/name/url parsed from the `origin` remote of the local checkout. */
export function getLocalGitInfo(cwd: string): GitInfo | undefined {
- const remote = git('remote get-url origin', cwd)
+ const remote = git(['remote', 'get-url', 'origin'], cwd)
return remote ? parseGitRemote(remote) : undefined
}
@@ -74,3 +73,32 @@ export function getGitEnv(): GitInfo | undefined {
return { name, owner, url: `https://${provider || 'github'}.com/${owner}/${name}` }
}
+
+/**
+ * The last commit touching `dir`, or `undefined`.
+ *
+ * Unverified on purpose. CI clones shallowly, and when the last commit touching `dir` predates the
+ * fetched window git answers with the shallow boundary commit rather than nothing — at depth 1,
+ * that is HEAD for every path. Confirm the answer with {@link getTreeSha} before trusting it to
+ * name a commit's content.
+ */
+export function getLastCommit(cwd: string, dir: string): string | undefined {
+ const sha = git(['log', '-1', '--format=%H', '--', dir], cwd)
+ return sha && /^[0-9a-f]{40}$/.test(sha) ? sha : undefined
+}
+
+/** Tree object id of `[:`, or `undefined` when the ref or the path is not in this checkout. */
+export function getTreeSha(cwd: string, ref: string, dir: string): string | undefined {
+ return git(['rev-parse', `${ref}:${dir}`], cwd)
+}
+
+/** Whether `ref` has a parent in this checkout. `false` at a shallow-clone boundary. */
+export function hasParent(cwd: string, ref: string): boolean {
+ return Boolean(git(['rev-parse', '--verify', `${ref}^`], cwd))
+}
+
+/** The commit checked out here, falling back to the CI-provided one. */
+export function headCommit(cwd: string): string | undefined {
+ const sha = git(['rev-parse', 'HEAD'], cwd) || process.env.VERCEL_GIT_COMMIT_SHA
+ return sha && /^[0-9a-f]{40}$/.test(sha) ? sha : undefined
+}
diff --git a/utils/github.ts b/utils/github.ts
new file mode 100644
index 0000000..24cfec0
--- /dev/null
+++ b/utils/github.ts
@@ -0,0 +1,36 @@
+export interface LastContentCommitOptions {
+ /** `owner/name` of the content repository. */
+ repo: string
+ /** Content directory; leading and trailing slashes are trimmed. */
+ path: string
+ /** Branch or commit to walk history from. */
+ ref: string
+ token?: string
+}
+
+/**
+ * The last commit reachable from `ref` that touched `path`.
+ */
+export async function fetchLastContentCommit(opts: LastContentCommitOptions): Promise {
+ const query = new URLSearchParams({
+ sha: opts.ref,
+ path: opts.path.replace(/^\/+|\/+$/g, ''),
+ per_page: '1',
+ })
+
+ const response = await fetch(`https://api.github.com/repos/${opts.repo}/commits?${query}`, {
+ headers: {
+ Accept: 'application/vnd.github+json',
+ ...(opts.token ? { Authorization: `Bearer ${opts.token}` } : {}),
+ },
+ })
+
+ if (!response.ok) {
+ throw Object.assign(new Error(`GitHub commits query failed with ${response.status}`), {
+ statusCode: response.status,
+ })
+ }
+
+ const commits = (await response.json()) as Array<{ sha?: string }>
+ return commits[0]?.sha
+}
diff --git a/utils/icons.ts b/utils/icons.ts
index 4c3d517..88e761e 100644
--- a/utils/icons.ts
+++ b/utils/icons.ts
@@ -1,33 +1,19 @@
-import { readFileSync } from 'node:fs'
import { resolveModulePath } from 'exsolve'
-// Icon collections this layer's components draw from: `lucide` (UI affordances), `simple-icons` (brand marks),
-// `vscode-icons` (the file-type icons Nuxt UI's `CodeIcon` derives from a filename).
-export const LAYER_ICON_COLLECTIONS = ['lucide', 'simple-icons', 'vscode-icons']
-
-/** Parsed collections, memoized — the client-bundle template regenerates in dev. */
-let cache: IconifyJSONish[] | undefined
-
-/** The shape `@nuxt/icon` accepts in `customCollections` (a raw `IconifyJSON`). */
-interface IconifyJSONish {
- prefix: string
- icons: Record
- [key: string]: unknown
-}
+// Collections served by this layer's `/api/_nuxt_icon` endpoint.
+// - `lucide`: UI affordances.
+// - `simple-icons`, `logos`: brand marks, mostly reached from consumer content.
+// - `vscode-icons`: the file-type icons Nuxt UI's `CodeIcon` derives from a filename.
+// - `unjs`: icons from UnJS's `unjs/icons` repository.
+// - `logos`: icons from logos.
+export const LAYER_ICON_COLLECTIONS = ['lucide', 'simple-icons', 'vscode-icons', 'logos', 'unjs']
/**
- * Load the layer's icon collections as data, resolved from the layer itself.
- *
- * `@nuxt/icon` discovers `@iconify-json/*` only by walking `node_modules/@iconify-json` up from the consuming
- * app's `rootDir`/`workspaceDir` (`modulesDir` is never consulted — see `getResolvePaths`). These packages are
- * dependencies of *this layer*, so under a non-hoisting install (pnpm's default `isolated` linker) they sit in
- * the virtual store, out of reach: zero collections, an empty client bundle, every icon fetched from
- * api.iconify.design at runtime. Done unconditionally, so behaviour is the same however the layer is consumed.
+ * Nitro aliases pinning each collection to the copy installed beside this layer.
*/
-export function layerIconCollections(): IconifyJSONish[] {
- cache ??= LAYER_ICON_COLLECTIONS.map((prefix) => {
- const path = resolveModulePath(`@iconify-json/${prefix}/icons.json`, { from: import.meta.url })
- return JSON.parse(readFileSync(path, 'utf8')) as IconifyJSONish
- })
- return cache
+export function layerIconAliases(): Record {
+ return Object.fromEntries(LAYER_ICON_COLLECTIONS.map((prefix) => {
+ const id = `@iconify-json/${prefix}/icons.json`
+ return [id, resolveModulePath(id, { from: import.meta.url })]
+ }))
}
diff --git a/utils/meta.ts b/utils/meta.ts
deleted file mode 100644
index fbdd97e..0000000
--- a/utils/meta.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { readFile } from 'node:fs/promises'
-import { resolve } from 'pathe'
-import { withHttps } from 'ufo'
-
-/** Infer the public site URL from the deployment platform env. */
-export function inferSiteURL(): string | undefined {
- // https://github.com/unjs/std-env/issues/59
- const url =
- process.env.NUXT_PUBLIC_SITE_URL ||
- process.env.NUXT_SITE_URL ||
- process.env.VERCEL_PROJECT_PRODUCTION_URL ||
- process.env.VERCEL_BRANCH_URL ||
- process.env.VERCEL_URL ||
- process.env.URL || // Netlify
- process.env.CI_PAGES_URL || // GitLab Pages
- process.env.CF_PAGES_URL // Cloudflare Pages
-
- return url ? withHttps(url) : undefined
-}
-
-export async function getPackageJsonMetadata(
- dir: string
-): Promise<{ name?: string; description?: string; version?: string }> {
- try {
- const parsed = JSON.parse(await readFile(resolve(dir, 'package.json'), 'utf-8'))
- return { name: parsed.name, description: parsed.description, version: parsed.version }
- } catch {
- return {}
- }
-}
diff --git a/utils/first-leaf.ts b/utils/navigation.ts
similarity index 100%
rename from utils/first-leaf.ts
rename to utils/navigation.ts
]