diff --git a/.changeset/satteri-ctx-data-metadata.md b/.changeset/satteri-ctx-data-metadata.md new file mode 100644 index 000000000000..0a251537ff53 --- /dev/null +++ b/.changeset/satteri-ctx-data-metadata.md @@ -0,0 +1,8 @@ +--- +'@astrojs/markdown-satteri': patch +'@astrojs/mdx': minor +--- + +Adds support for [modifying frontmatter programmatically](https://docs.astro.build/en/guides/markdown-content/#modifying-frontmatter-programmatically) with the default Markdown processor. + +A Sätteri plugin can now read and mutate `ctx.data.astro.frontmatter`, and Astro uses the result as the page's frontmatter, in both Markdown and MDX. diff --git a/packages/integrations/mdx/package.json b/packages/integrations/mdx/package.json index b01789891290..38d30481d6ec 100644 --- a/packages/integrations/mdx/package.json +++ b/packages/integrations/mdx/package.json @@ -77,7 +77,7 @@ "remark-math": "^6.0.0", "remark-rehype": "^11.1.2", "remark-toc": "^9.0.0", - "satteri": "^0.9.0", + "satteri": "^0.9.1", "shiki": "^4.0.2", "unified": "^11.0.5", "vite": "^8.0.13" diff --git a/packages/integrations/mdx/src/satteri/hast-astro-metadata.ts b/packages/integrations/mdx/src/satteri/hast-astro-metadata.ts index c6ddb95c0e98..c9ee8c627207 100644 --- a/packages/integrations/mdx/src/satteri/hast-astro-metadata.ts +++ b/packages/integrations/mdx/src/satteri/hast-astro-metadata.ts @@ -1,5 +1,5 @@ import type { AstroMetadata } from '@astrojs/internal-helpers/markdown'; -import { resolvePath } from 'astro/markdown'; +import { createDefaultAstroMetadata, resolvePath } from 'astro/markdown'; import type { Identifier, Literal } from 'estree'; import { defineHastPlugin, @@ -11,6 +11,12 @@ import { findAttrValue, hasDirective, isComponent, type MdxJsxHastNode } from '. export type { AstroMetadata }; +declare module 'satteri' { + interface DataMap { + __astroMetadata: AstroMetadata; + } +} + type ImportSpecifier = { local: string; imported: string }; type MatchedImport = { name: string; path: string }; @@ -83,7 +89,6 @@ function findMatchingImport( function processJsxNode( node: MdxJsxHastNode, ctx: HastVisitorContext, - metadata: AstroMetadata, imports: Map>, filePath: string, ) { @@ -118,6 +123,7 @@ function processJsxNode( const exportName = matchedImport.name === '*' ? tagName.split('.').slice(1).join('.') : matchedImport.name; + const metadata = (ctx.data.__astroMetadata ??= createDefaultAstroMetadata()); if (hasClient && findAttrValue(node, 'client:only') !== null) { metadata.clientOnlyComponents.push({ exportName: matchedImport.name, @@ -152,10 +158,7 @@ function processJsxNode( } } -export function createAstroMetadataPlugin( - metadata: AstroMetadata, - filePath: string, -): HastPluginDefinition { +export function createAstroMetadataPlugin(filePath: string): HastPluginDefinition { const imports = new Map>(); return defineHastPlugin({ name: 'astro-metadata', @@ -169,14 +172,14 @@ export function createAstroMetadataPlugin( mdxJsxFlowElement: { filter: [], visit(node, ctx) { - processJsxNode(node, ctx, metadata, imports, filePath); + processJsxNode(node, ctx, imports, filePath); }, }, mdxJsxTextElement: { filter: [], visit(node, ctx) { - processJsxNode(node, ctx, metadata, imports, filePath); + processJsxNode(node, ctx, imports, filePath); }, }, }); diff --git a/packages/integrations/mdx/src/satteri/hast-images-to-component.ts b/packages/integrations/mdx/src/satteri/hast-images-to-component.ts index ec074451e6b1..a7e7857a088a 100644 --- a/packages/integrations/mdx/src/satteri/hast-images-to-component.ts +++ b/packages/integrations/mdx/src/satteri/hast-images-to-component.ts @@ -13,21 +13,20 @@ export interface ImageImportInfo { } export function createImageToComponentPlugin( - localImagePaths: Set, - remoteImagePaths: Set, imageImportInfo: ImageImportInfo, ): HastPluginDefinition { return defineHastPlugin({ name: 'image-to-component', element: { filter: ['img'], - visit(node) { + visit(node, ctx) { const src = node.properties?.src; if (typeof src !== 'string') return; const decodedSrc = decodeURI(src); - const isLocal = localImagePaths.has(decodedSrc); - const isRemote = remoteImagePaths.has(decodedSrc); + const astro = ctx.data.astro; + const isLocal = astro?.localImagePaths.has(decodedSrc) ?? false; + const isRemote = astro?.remoteImagePaths.has(decodedSrc) ?? false; if (!isLocal && !isRemote) return; const attrs: MdxJsxAttributeNode[] = []; diff --git a/packages/integrations/mdx/src/satteri/index.ts b/packages/integrations/mdx/src/satteri/index.ts index 861d6003c6e6..de9745623067 100644 --- a/packages/integrations/mdx/src/satteri/index.ts +++ b/packages/integrations/mdx/src/satteri/index.ts @@ -1,6 +1,7 @@ import { pathToFileURL } from 'node:url'; +import { isFrontmatterValid } from '@astrojs/internal-helpers/frontmatter'; import type { MarkdownHeading } from '@astrojs/internal-helpers/markdown'; -import type { SatteriResolvedOptions } from '@astrojs/markdown-satteri'; +import type { SatteriAstroData, SatteriResolvedOptions } from '@astrojs/markdown-satteri'; import { satteriCollectImagesPlugin, satteriCreateHighlightFn, @@ -67,32 +68,28 @@ export function createMdxProcessor( } if (initPromise) await initPromise; - const headings: MarkdownHeading[] = []; - const localImagePaths = new Set(); - const remoteImagePaths = new Set(); - - const astroMetadata = createDefaultAstroMetadata(); + const astroData: SatteriAstroData = { + frontmatter, + headings: [], + localImagePaths: new Set(), + remoteImagePaths: new Set(), + }; - const collectImages = satteriCollectImagesPlugin(localImagePaths, remoteImagePaths); - const headingIds = satteriHeadingIdsPlugin(headings, frontmatter); - const astroMeta = createAstroMetadataPlugin(astroMetadata, filePath); + const collectImages = satteriCollectImagesPlugin(); + const headingIds = satteriHeadingIdsPlugin(); + const astroMeta = createAstroMetadataPlugin(filePath); const imageImportInfo: ImageImportInfo = { importedImages: new Map(), hasImages: false, }; - const imageToComponent = createImageToComponentPlugin( - localImagePaths, - remoteImagePaths, - imageImportInfo, - ); + const imageToComponent = createImageToComponentPlugin(imageImportInfo); const syntaxHighlight = mdxOptions.syntaxHighlight; const excludeLangs = typeof syntaxHighlight === 'object' ? syntaxHighlight.excludeLangs : undefined; - const allMdastPlugins: MdastPluginDefinition[] = satteriOptions.mdastPlugins.length - ? [collectImages, ...satteriOptions.mdastPlugins] - : [collectImages]; + // Collect last so image-URL rewrites by user plugins are captured. + const allMdastPlugins: MdastPluginDefinition[] = [...satteriOptions.mdastPlugins, collectImages]; const hastPlugins: HastPluginDefinition[] = []; if (highlightFn) { @@ -136,9 +133,23 @@ export function createMdxProcessor( }, fileURL: pathToFileURL(filePath), jsxImportSource: 'astro', + data: { astro: astroData }, }); let compiled = mdxResult.code; + // Read the returned bag, not the seeded reference, so a plugin that replaces it is honored. + const astro = mdxResult.data.astro; + const headings = astro?.headings ?? []; + const astroMetadata = mdxResult.data.__astroMetadata ?? createDefaultAstroMetadata(); + + // Plugins may have mutated frontmatter; emit the final value. + const resolvedFrontmatter = astro?.frontmatter; + if (!resolvedFrontmatter || !isFrontmatterValid(resolvedFrontmatter)) { + throw new Error( + '[MDX] A Sätteri plugin attempted to inject invalid frontmatter. Ensure `ctx.data.astro.frontmatter` is a valid object that is not `null` or `undefined`.', + ); + } + compiled = compiled.replace(/^export default MDXContent;\s*$/m, ''); if (imageImportInfo.hasImages) { @@ -152,14 +163,14 @@ export function createMdxProcessor( compiled += `\nexport const ${USES_ASTRO_IMAGE_FLAG} = true;`; } - compiled += `\nexport const frontmatter = ${JSON.stringify(frontmatter)};`; + compiled += `\nexport const frontmatter = ${JSON.stringify(resolvedFrontmatter)};`; compiled += `\nexport function getHeadings() { return ${JSON.stringify(headings)}; }`; - if (frontmatter.layout) { + if (resolvedFrontmatter.layout) { compiled = compiled.replace(/^function MDXContent\(/m, 'function __OriginalMDXContent__('); compiled += ` import { jsx as __astro_layout_jsx__ } from 'astro/jsx-runtime'; -import __astro_layout_component__ from ${JSON.stringify(frontmatter.layout)}; +import __astro_layout_component__ from ${JSON.stringify(resolvedFrontmatter.layout)}; export default function MDXContent(props) { const content = __OriginalMDXContent__(props); const { layout, ...frontmatterContent } = frontmatter; diff --git a/packages/integrations/mdx/test/fixtures/mdx-get-headings/src/pages/pages.json.js b/packages/integrations/mdx/test/fixtures/mdx-get-headings/src/pages/pages.json.js index e57192c6151d..aeabce7564ae 100644 --- a/packages/integrations/mdx/test/fixtures/mdx-get-headings/src/pages/pages.json.js +++ b/packages/integrations/mdx/test/fixtures/mdx-get-headings/src/pages/pages.json.js @@ -1,8 +1,8 @@ export async function GET() { const mdxPages = await import.meta.glob('./*.mdx', { eager: true }); + const entries = Object.entries(mdxPages ?? {}); return Response.json({ - headingsByPage: Object.fromEntries( - Object.entries(mdxPages ?? {}).map(([k, v]) => [k, v?.getHeadings()]) - ), + headingsByPage: Object.fromEntries(entries.map(([k, v]) => [k, v?.getHeadings()])), + frontmatterByPage: Object.fromEntries(entries.map(([k, v]) => [k, v?.frontmatter])), }); } diff --git a/packages/integrations/mdx/test/mdx-satteri.test.ts b/packages/integrations/mdx/test/mdx-satteri.test.ts index 92d3e9e9d6fc..952f9184c477 100644 --- a/packages/integrations/mdx/test/mdx-satteri.test.ts +++ b/packages/integrations/mdx/test/mdx-satteri.test.ts @@ -3,8 +3,16 @@ import { before, describe, it } from 'node:test'; import mdx from '@astrojs/mdx'; import { satteri } from '@astrojs/markdown-satteri'; import { parseHTML } from 'linkedom'; +import type { MdastPluginDefinition } from 'satteri'; import { loadFixture, type Fixture } from './test-utils.ts'; +const injectFrontmatter: MdastPluginDefinition = { + name: 'inject-frontmatter', + heading(_node, ctx) { + ctx.data.astro!.frontmatter.injected = 'from-plugin'; + }, +}; + describe('MDX with the Sätteri processor', () => { let fixture: Fixture; @@ -13,7 +21,7 @@ describe('MDX with the Sätteri processor', () => { root: new URL('./fixtures/mdx-get-headings/', import.meta.url), integrations: [mdx()], markdown: { - processor: satteri(), + processor: satteri({ mdastPlugins: [injectFrontmatter] }), }, }); @@ -37,4 +45,11 @@ describe('MDX with the Sätteri processor', () => { assert.ok(slugs.includes('section-1')); assert.ok(slugs.includes('section-2')); }); + + it('lets a Sätteri plugin modify frontmatter, exposed via the page export', async () => { + const { frontmatterByPage } = JSON.parse(await fixture.readFile('/pages.json')); + assert.equal(frontmatterByPage['./test.mdx'].injected, 'from-plugin'); + assert.equal(frontmatterByPage['./test-with-frontmatter.mdx'].injected, 'from-plugin'); + assert.equal(frontmatterByPage['./test-with-frontmatter.mdx'].title, 'The Frontmatter Title'); + }); }); diff --git a/packages/markdown/satteri/package.json b/packages/markdown/satteri/package.json index 06048f43448e..4be589d87b7e 100644 --- a/packages/markdown/satteri/package.json +++ b/packages/markdown/satteri/package.json @@ -29,7 +29,7 @@ "@astrojs/internal-helpers": "workspace:*", "@astrojs/prism": "workspace:*", "github-slugger": "^2.0.0", - "satteri": "^0.9.0" + "satteri": "^0.9.1" }, "devDependencies": { "astro-scripts": "workspace:*" diff --git a/packages/markdown/satteri/src/index.ts b/packages/markdown/satteri/src/index.ts index 529187cefd89..fbdcff403d4c 100644 --- a/packages/markdown/satteri/src/index.ts +++ b/packages/markdown/satteri/src/index.ts @@ -8,6 +8,7 @@ export { createHighlightFn as satteriCreateHighlightFn, collectHastText as satteriCollectHastText, makeFragmentNode as satteriMakeFragmentNode, + type SatteriAstroData, createSatteriMarkdownProcessor, type SatteriMarkdownProcessorOptions, } from './satteri-processor.js'; diff --git a/packages/markdown/satteri/src/satteri-processor.ts b/packages/markdown/satteri/src/satteri-processor.ts index 041bc42ac0e1..894c4dc34038 100644 --- a/packages/markdown/satteri/src/satteri-processor.ts +++ b/packages/markdown/satteri/src/satteri-processor.ts @@ -15,6 +15,19 @@ import type { type HighlightFn = (code: string, lang: string, meta?: string) => Promise; +declare module 'satteri' { + interface DataMap { + astro: SatteriAstroData; + } +} + +export interface SatteriAstroData { + frontmatter: Record; + headings: MarkdownHeading[]; + localImagePaths: Set; + remoteImagePaths: Set; +} + let satteri: typeof import('satteri') | undefined; // Loaded lazily so the Sätteri Rust/WASM binary isn't pulled in unless the @@ -25,24 +38,23 @@ async function loadSatteri(): Promise { } export function createCollectImagesPlugin( - localImagePaths: Set, - remoteImagePaths: Set, image: AstroMarkdownOptions['image'] = {}, ): MdastPluginDefinition { const domains = image?.domains ?? []; const remotePatterns = image?.remotePatterns ?? []; return { name: 'collect-images', - image(node) { + image(node, ctx) { const url = node.url ? decodeURI(node.url) : undefined; if (!url) return; + const astro = ctx.data.astro; if (URL.canParse(url)) { if (isRemoteAllowed(url, { domains, remotePatterns })) { - remoteImagePaths.add(url); + astro?.remoteImagePaths.add(url); } } else if (!url.startsWith('/')) { - localImagePaths.add(url); + astro?.localImagePaths.add(url); } }, }; @@ -103,25 +115,22 @@ export function makeFragmentNode(html: string): HastNode { } as unknown as HastNode; } -export function createHeadingIdsPlugin( - headings?: MarkdownHeading[], - frontmatter?: Record, -): HastPluginDefinition { +export function createHeadingIdsPlugin(): HastPluginDefinition { const slugger = new Slugger(); return { name: 'heading-ids', element: { filter: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'], visit(node, ctx) { + const astro = ctx.data.astro; const rawText = ctx.textContent(node); - const text = - frontmatter && rawText.includes('frontmatter') - ? collectHastText(node, frontmatter) - : rawText; + const text = rawText.includes('frontmatter') + ? collectHastText(node, astro?.frontmatter ?? {}) + : rawText; const existingId = node.properties?.id; const slug = typeof existingId === 'string' ? existingId : slugger.slug(text); const depth = Number.parseInt(node.tagName[1], 10); - headings?.push({ depth, slug, text }); + astro?.headings.push({ depth, slug, text }); if (typeof existingId !== 'string') { ctx.setProperty(node, 'id', slug); } @@ -137,10 +146,7 @@ function makeRawNode(html: string): HastNode { const HAST_PRESERVED_PROPERTIES = new Set(['className', 'htmlFor']); /** Tags with `__ASTRO_IMAGE_` for astro's vite-plugin-markdown to replace with the processed image's attributes. */ -export function createImageMarkerPlugin( - localImagePaths: Set, - remoteImagePaths: Set, -): HastPluginDefinition { +export function createImageMarkerPlugin(): HastPluginDefinition { const indexBySrc = new Map(); return { name: 'image-marker', @@ -152,8 +158,9 @@ export function createImageMarkerPlugin( if (!rawSrc) return; const src = decodeURI(rawSrc); - const isLocal = localImagePaths.has(src); - const isRemote = !isLocal && remoteImagePaths.has(src); + const astro = ctx.data.astro; + const isLocal = astro?.localImagePaths.has(src) ?? false; + const isRemote = !isLocal && (astro?.remoteImagePaths.has(src) ?? false); if (!isLocal && !isRemote) return; const { src: _src, ...rest } = props; @@ -282,14 +289,17 @@ export async function createSatteriMarkdownProcessor( return { async render(content, renderOpts) { - const headings: MarkdownHeading[] = []; - const localImagePaths = new Set(); - const remoteImagePaths = new Set(); - const frontmatter = renderOpts?.frontmatter ?? {}; + const astro: SatteriAstroData = { + frontmatter: renderOpts?.frontmatter ?? {}, + headings: [], + localImagePaths: new Set(), + remoteImagePaths: new Set(), + }; + // Collect last so image-URL rewrites by user plugins are captured. const allMdastPlugins: MdastPluginDefinition[] = [ - createCollectImagesPlugin(localImagePaths, remoteImagePaths, opts?.image), ...userMdastPlugins, + createCollectImagesPlugin(opts?.image), ]; const hastPlugins: HastPluginDefinition[] = []; @@ -297,10 +307,10 @@ export async function createSatteriMarkdownProcessor( hastPlugins.push(createHighlightPlugin(highlightFn, syntaxHighlightExcludeLangs)); } hastPlugins.push(...userHastPlugins); - hastPlugins.push(createImageMarkerPlugin(localImagePaths, remoteImagePaths)); - hastPlugins.push(createHeadingIdsPlugin(headings, frontmatter)); + hastPlugins.push(createImageMarkerPlugin()); + hastPlugins.push(createHeadingIdsPlugin()); - const { html } = await s.markdownToHtml(content, { + const { html, data } = await s.markdownToHtml(content, { mdastPlugins: allMdastPlugins, hastPlugins, features: { @@ -309,15 +319,19 @@ export async function createSatteriMarkdownProcessor( ...userFeatures, }, fileURL: renderOpts?.fileURL, + data: { astro }, }); + // Read the returned bag, not the seeded reference, so a plugin that replaces + // `ctx.data.astro` wholesale is honored. + const result = data.astro; return { code: html, metadata: { - headings, - localImagePaths: Array.from(localImagePaths), - remoteImagePaths: Array.from(remoteImagePaths), - frontmatter, + headings: result?.headings ?? [], + localImagePaths: result ? Array.from(result.localImagePaths) : [], + remoteImagePaths: result ? Array.from(result.remoteImagePaths) : [], + frontmatter: result?.frontmatter ?? {}, }, }; }, diff --git a/packages/markdown/satteri/test/markdown.test.ts b/packages/markdown/satteri/test/markdown.test.ts index 7a2b44a31f1a..b4b4d2a3825b 100644 --- a/packages/markdown/satteri/test/markdown.test.ts +++ b/packages/markdown/satteri/test/markdown.test.ts @@ -1,6 +1,6 @@ import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; -import type { HastPluginDefinition } from 'satteri'; +import type { HastPluginDefinition, MdastPluginDefinition } from 'satteri'; import { createSatteriMarkdownProcessor, satteriHeadingIdsPlugin } from '../dist/index.js'; describe('satteri markdown', () => { @@ -91,4 +91,38 @@ describe('satteri markdown', () => { assert.match(code, /

Title<\/h1>/); assert.deepEqual(metadata.headings, [{ depth: 1, slug: 'custom-id', text: 'Title' }]); }); + + it('collects image paths after user mdast plugins resolve them', async () => { + const resolveImage: MdastPluginDefinition = { + name: 'resolve-image', + image(node, ctx) { + if (node.url === './unresolved.png') { + ctx.setProperty(node, 'url', './resolved.png'); + } + }, + }; + const processor = await createSatteriMarkdownProcessor({ + mdastPlugins: [resolveImage], + }); + const { metadata } = await processor.render('![alt](./unresolved.png)'); + assert.deepEqual(metadata.localImagePaths, ['./resolved.png']); + }); + + it('lets a plugin read seeded frontmatter and modify it through ctx.data', async () => { + const injectPlugin: MdastPluginDefinition = { + name: 'inject-frontmatter', + heading(_node, ctx) { + const astro = ctx.data.astro!; + astro.frontmatter.injected = String(astro.frontmatter.title).toUpperCase(); + }, + }; + const processor = await createSatteriMarkdownProcessor({ + mdastPlugins: [injectPlugin], + }); + const { metadata } = await processor.render('# Heading', { + frontmatter: { title: 'hello' }, + }); + assert.equal(metadata.frontmatter.title, 'hello'); + assert.equal(metadata.frontmatter.injected, 'HELLO'); + }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 292c53b30839..62e5823d0517 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -127,7 +127,7 @@ importers: version: 5.2.0(tinybench@2.9.0)(vite@8.0.14)(vitest@4.1.0) vitest: specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.98.0)(tsx@4.22.3)(yaml@2.9.0) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(vite@8.0.14) benchmark/packages/adapter: dependencies: @@ -246,7 +246,7 @@ importers: version: 18.3.1(react@18.3.1) vitest: specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.98.0)(tsx@4.22.3)(yaml@2.9.0) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(vite@8.0.14) devDependencies: '@types/react': specifier: ^18.3.28 @@ -782,7 +782,7 @@ importers: version: 11.0.5 vitest: specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.98.0)(tsx@4.22.3)(yaml@2.9.0) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(vite@8.0.14) optionalDependencies: sharp: specifier: ^0.34.0 @@ -4100,7 +4100,7 @@ importers: version: link:../../.. vitest: specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.98.0)(tsx@4.22.3)(yaml@2.9.0) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(vite@8.0.14) packages/astro/test/fixtures/vue-component: dependencies: @@ -5019,8 +5019,8 @@ importers: specifier: ^9.0.0 version: 9.0.0 satteri: - specifier: ^0.9.0 - version: 0.9.0 + specifier: ^0.9.1 + version: 0.9.1 shiki: specifier: ^4.0.2 version: 4.0.2 @@ -6637,8 +6637,8 @@ importers: specifier: ^2.0.0 version: 2.0.0 satteri: - specifier: ^0.9.0 - version: 0.9.0 + specifier: ^0.9.1 + version: 0.9.1 devDependencies: astro-scripts: specifier: workspace:* @@ -7228,29 +7228,29 @@ packages: '@borewit/text-codec@0.2.2': resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==} - '@bruits/satteri-darwin-arm64@0.9.0': - resolution: {integrity: sha512-fJtb3K+D8OvtjeZO2CB58gS50F6wV2Ve/BHoyp8D4d7sUhaSVoKqn0ma5wWLlIFAjdWDfLr5wx/KTukT7lgpQA==} + '@bruits/satteri-darwin-arm64@0.9.1': + resolution: {integrity: sha512-NE4qC2sRd0+R+oPMsKcUikIvWTGpAV16fSXNBoMSW7nK7Pd9e/ZGB7M8knep83pFmGmOb/5NbGKiVIh3oLbljw==} cpu: [arm64] os: [darwin] - '@bruits/satteri-darwin-x64@0.9.0': - resolution: {integrity: sha512-jj6Y9Hnwi1y276QqOK/aNUltDbKJqzuJw62yisRPkYSA4FHE6Di0LDsSAwK4n7S//jP0sn1Q2KsDNYhCQ9WNIw==} + '@bruits/satteri-darwin-x64@0.9.1': + resolution: {integrity: sha512-Am8z5nX0L/sJR/n7np+5rMVP434MOBe3zlU+IO8fXbv2UaPNRCrEQPMS6lyxCj7dZHQI2AynSJw3v4fgQE8xSQ==} cpu: [x64] os: [darwin] - '@bruits/satteri-linux-x64-gnu@0.9.0': - resolution: {integrity: sha512-AwBHO06U1eFqS1UoqZM9qowFDdisnf355JuahAby9LN/p9R7swlQV9QqIRPPHeFEm5ECoDNlCI5TPNWqP34+6Q==} + '@bruits/satteri-linux-x64-gnu@0.9.1': + resolution: {integrity: sha512-Bivw60+SIfmlaU9wEZ39HAcyPh1xU9TvOrz4KJgc+ziRStQs4EzYoWW4Eh9aSdiol+xV0vjEWYuA79aF3dr7kg==} cpu: [x64] os: [linux] libc: [glibc] - '@bruits/satteri-wasm32-wasi@0.9.0': - resolution: {integrity: sha512-/6ga1dnKqWEeZ387eBmlfhOqAn1BPx/93UQrp1dpHtFM9brvQCZT3RBe5Gazd4BcloRebvXZW6vvnePVhcaKGw==} + '@bruits/satteri-wasm32-wasi@0.9.1': + resolution: {integrity: sha512-lEFk5ebh5SxRquA5RJisEoMoDmOiSnS10PGgXgXeVlWgF8KWKI9D3hSzVqNjEg/qKOjHcNdjIfyx/03Wrl6J3g==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@bruits/satteri-win32-x64-msvc@0.9.0': - resolution: {integrity: sha512-k3U1EHIr5sNCsBxmXvLUj1xZT3Pf3gLXabWUT4iH4W9CLIbzAMu1lYF3c4pKS6GRYPMsSNfz78/vNjEkRW396A==} + '@bruits/satteri-win32-x64-msvc@0.9.1': + resolution: {integrity: sha512-YBhm8yARopswAPeTih11BzMxWVQFD5CI9Yryp6HWepi6F/CeMycqxv18DXrj9U2fDDlbVGwT2IiEM2UPBjIPDQ==} cpu: [x64] os: [win32] @@ -14276,8 +14276,8 @@ packages: engines: {node: '>=14.0.0'} hasBin: true - satteri@0.9.0: - resolution: {integrity: sha512-+MA67ZhGYSrMvEFCciKKnyB6XeH1SJ7xeYJkTah4qzRjuo1ZehrCIn2ksysks/XA6Ez2gnH0SnKeMEAVVK+g+Q==} + satteri@0.9.1: + resolution: {integrity: sha512-0oIBjwDxWvz8ePRSBxv8vEdZ0GI25/UcSq11y4651tJztUAmZlIdPjFx8luqBAM22g8YKVr5R17KaaZ2kIfLrw==} sax@1.6.0: resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} @@ -15309,6 +15309,7 @@ packages: '@vitest/ui': 4.1.0 happy-dom: '*' jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0-0 peerDependenciesMeta: '@edge-runtime/vm': optional: true @@ -16417,23 +16418,23 @@ snapshots: '@borewit/text-codec@0.2.2': {} - '@bruits/satteri-darwin-arm64@0.9.0': + '@bruits/satteri-darwin-arm64@0.9.1': optional: true - '@bruits/satteri-darwin-x64@0.9.0': + '@bruits/satteri-darwin-x64@0.9.1': optional: true - '@bruits/satteri-linux-x64-gnu@0.9.0': + '@bruits/satteri-linux-x64-gnu@0.9.1': optional: true - '@bruits/satteri-wasm32-wasi@0.9.0': + '@bruits/satteri-wasm32-wasi@0.9.1': dependencies: '@emnapi/core': 1.9.1 '@emnapi/runtime': 1.9.1 '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1) optional: true - '@bruits/satteri-win32-x64-msvc@0.9.0': + '@bruits/satteri-win32-x64-msvc@0.9.1': optional: true '@capsizecss/unpack@4.0.0': @@ -16661,7 +16662,7 @@ snapshots: '@codspeed/core': 5.2.0 tinybench: 2.9.0 vite: 8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.98.0)(tsx@4.22.3)(yaml@2.9.0) - vitest: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.98.0)(tsx@4.22.3)(yaml@2.9.0) + vitest: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(vite@8.0.14) transitivePeerDependencies: - debug @@ -17811,7 +17812,7 @@ snapshots: dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@tybys/wasm-util': 0.10.1 + '@tybys/wasm-util': 0.10.2 optional: true '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': @@ -18926,7 +18927,7 @@ snapshots: svelte: 5.55.3 optionalDependencies: vite: 8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.98.0)(tsx@4.22.3)(yaml@2.9.0) - vitest: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.98.0)(tsx@4.22.3)(yaml@2.9.0) + vitest: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(vite@8.0.14) '@textlint/ast-node-types@15.5.1': {} @@ -24509,18 +24510,18 @@ snapshots: optionalDependencies: '@parcel/watcher': 2.5.6 - satteri@0.9.0: + satteri@0.9.1: dependencies: '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.4 '@types/mdast': 4.0.4 '@types/unist': 3.0.3 optionalDependencies: - '@bruits/satteri-darwin-arm64': 0.9.0 - '@bruits/satteri-darwin-x64': 0.9.0 - '@bruits/satteri-linux-x64-gnu': 0.9.0 - '@bruits/satteri-wasm32-wasi': 0.9.0 - '@bruits/satteri-win32-x64-msvc': 0.9.0 + '@bruits/satteri-darwin-arm64': 0.9.1 + '@bruits/satteri-darwin-x64': 0.9.1 + '@bruits/satteri-linux-x64-gnu': 0.9.1 + '@bruits/satteri-wasm32-wasi': 0.9.1 + '@bruits/satteri-win32-x64-msvc': 0.9.1 sax@1.6.0: {} @@ -25563,7 +25564,7 @@ snapshots: optionalDependencies: vite: 8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.98.0)(tsx@4.22.3)(yaml@2.9.0) - vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.98.0)(tsx@4.22.3)(yaml@2.9.0): + vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(vite@8.0.14): dependencies: '@vitest/expect': 4.1.0 '@vitest/mocker': 4.1.0(vite@8.0.14) @@ -25589,18 +25590,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@types/node': 22.19.19 transitivePeerDependencies: - - '@vitejs/devtools' - - esbuild - - jiti - - less - msw - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml vitest@5.0.0-beta.3(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(vite@8.0.14): dependencies: