diff --git a/.changeset/fix-i18n-fallback-redirect.md b/.changeset/fix-i18n-fallback-redirect.md new file mode 100644 index 000000000000..ca83a35382ab --- /dev/null +++ b/.changeset/fix-i18n-fallback-redirect.md @@ -0,0 +1,7 @@ +--- +'astro': patch +--- + +Fixes i18n fallback middleware intercepting non-404 responses + +The fallback middleware was triggering for all responses with status >= 300, including legitimate 3xx redirects, 403 forbidden, and 5xx server errors. This broke auth flows and form submissions on localized server routes. The fallback now correctly only triggers for 404 (page not found) responses. diff --git a/.changeset/humble-humans-sink.md b/.changeset/humble-humans-sink.md new file mode 100644 index 000000000000..92302e191c51 --- /dev/null +++ b/.changeset/humble-humans-sink.md @@ -0,0 +1,5 @@ +--- +'@astrojs/node': patch +--- + +Fixes an issue where static headers weren't correctly applied when the website uses `base`. diff --git a/packages/astro/src/i18n/fallback.ts b/packages/astro/src/i18n/fallback.ts index 8c2492275709..609c94a7d12c 100644 --- a/packages/astro/src/i18n/fallback.ts +++ b/packages/astro/src/i18n/fallback.ts @@ -54,8 +54,8 @@ export function computeFallbackRoute(options: ComputeFallbackRouteOptions): Fall base, } = options; - // Only apply fallback for 3xx+ status codes - if (responseStatus < 300) { + // Only apply fallback for 404 status codes (page not found in this locale) + if (responseStatus !== 404) { return { type: 'none' }; } diff --git a/packages/astro/src/i18n/index.ts b/packages/astro/src/i18n/index.ts index f8650ded6278..3db8fb1420d0 100644 --- a/packages/astro/src/i18n/index.ts +++ b/packages/astro/src/i18n/index.ts @@ -372,7 +372,7 @@ export function redirectToFallback({ fallbackType, }: MiddlewarePayload) { return async function (context: APIContext, response: Response): Promise { - if (response.status >= 300 && fallback) { + if (response.status === 404 && fallback) { const fallbackKeys = fallback ? Object.keys(fallback) : []; // we split the URL using the `/`, and then check in the returned array we have the locale const segments = context.url.pathname.split('/'); diff --git a/packages/astro/test/units/i18n/fallback.test.js b/packages/astro/test/units/i18n/fallback.test.js index 37ca981e1223..01b91856216a 100644 --- a/packages/astro/test/units/i18n/fallback.test.js +++ b/packages/astro/test/units/i18n/fallback.test.js @@ -4,8 +4,8 @@ import { computeFallbackRoute } from '../../../dist/i18n/fallback.js'; import { makeFallbackOptions } from './test-helpers.js'; describe('computeFallbackRoute', () => { - describe('when response status < 300', () => { - it('returns none (no fallback needed)', () => { + describe('when response status is not 404', () => { + it('returns none for 200 (success)', () => { const result = computeFallbackRoute( makeFallbackOptions({ pathname: '/es/missing', @@ -18,11 +18,50 @@ describe('computeFallbackRoute', () => { assert.equal(result.type, 'none'); }); - it('returns none for 299 status', () => { + it('returns none for 301 (redirect)', () => { const result = computeFallbackRoute( makeFallbackOptions({ - pathname: '/es/missing', - responseStatus: 299, + pathname: '/es/redirect', + responseStatus: 301, + currentLocale: 'es', + fallback: { es: 'en' }, + }), + ); + + assert.equal(result.type, 'none'); + }); + + it('returns none for 302 (temporary redirect)', () => { + const result = computeFallbackRoute( + makeFallbackOptions({ + pathname: '/es/redirect', + responseStatus: 302, + currentLocale: 'es', + fallback: { es: 'en' }, + }), + ); + + assert.equal(result.type, 'none'); + }); + + it('returns none for 403 (forbidden)', () => { + const result = computeFallbackRoute( + makeFallbackOptions({ + pathname: '/es/forbidden', + responseStatus: 403, + currentLocale: 'es', + fallback: { es: 'en' }, + }), + ); + + assert.equal(result.type, 'none'); + }); + + it('returns none for 500 (server error)', () => { + const result = computeFallbackRoute( + makeFallbackOptions({ + pathname: '/es/error', + responseStatus: 500, currentLocale: 'es', fallback: { es: 'en' }, }), @@ -148,7 +187,7 @@ describe('computeFallbackRoute', () => { assert.equal(result.pathname, '/es/missing'); }); - it('handles 3xx redirect status', () => { + it('only triggers for 404 status, not 3xx', () => { const result = computeFallbackRoute( makeFallbackOptions({ pathname: '/es/redirect', @@ -159,10 +198,10 @@ describe('computeFallbackRoute', () => { }), ); - assert.equal(result.type, 'redirect'); + assert.equal(result.type, 'none'); }); - it('handles 4xx status', () => { + it('triggers for 404 status', () => { const result = computeFallbackRoute( makeFallbackOptions({ pathname: '/es/notfound', @@ -176,7 +215,7 @@ describe('computeFallbackRoute', () => { assert.equal(result.type, 'redirect'); }); - it('handles 5xx status', () => { + it('only triggers for 404 status, not 5xx', () => { const result = computeFallbackRoute( makeFallbackOptions({ pathname: '/es/error', @@ -187,7 +226,7 @@ describe('computeFallbackRoute', () => { }), ); - assert.equal(result.type, 'redirect'); + assert.equal(result.type, 'none'); }); }); diff --git a/packages/astro/test/units/i18n/manual-routing.test.js b/packages/astro/test/units/i18n/manual-routing.test.js index 06b3d597ab09..e8038cd6f2eb 100644 --- a/packages/astro/test/units/i18n/manual-routing.test.js +++ b/packages/astro/test/units/i18n/manual-routing.test.js @@ -961,7 +961,7 @@ describe('notFound', () => { describe('redirectToFallback', () => { describe('basic fallback behavior', () => { - it('should return original response when status < 300', async () => { + it('should return original response when status is not 404', async () => { const payload = createMiddlewarePayload({ fallback: { es: 'en' }, }); @@ -974,7 +974,7 @@ describe('redirectToFallback', () => { assert.equal(response, originalResponse); }); - it('should redirect when status >= 300 and locale has fallback', async () => { + it('should redirect when status is 404 and locale has fallback', async () => { const payload = createMiddlewarePayload({ locales: ['en', 'es', 'fr'], defaultLocale: 'en', @@ -1087,7 +1087,7 @@ describe('redirectToFallback', () => { assert.equal(response.headers.get('Location'), '/search?q=test'); }); - it('should handle 3xx status codes', async () => { + it('should not redirect for 3xx status codes', async () => { const payload = createMiddlewarePayload({ locales: ['en', 'es'], fallback: { es: 'en' }, @@ -1099,10 +1099,10 @@ describe('redirectToFallback', () => { const response = await fallbackFn(context, originalResponse); - assert.equal(response.status, 302); + assert.equal(response, originalResponse); }); - it('should handle 4xx status codes', async () => { + it('should not redirect for non-404 4xx status codes', async () => { const payload = createMiddlewarePayload({ locales: ['en', 'es'], fallback: { es: 'en' }, @@ -1114,10 +1114,10 @@ describe('redirectToFallback', () => { const response = await fallbackFn(context, originalResponse); - assert.equal(response.status, 302); + assert.equal(response, originalResponse); }); - it('should handle 5xx status codes', async () => { + it('should not redirect for 5xx status codes', async () => { const payload = createMiddlewarePayload({ locales: ['en', 'es'], fallback: { es: 'en' }, @@ -1129,7 +1129,7 @@ describe('redirectToFallback', () => { const response = await fallbackFn(context, originalResponse); - assert.equal(response.status, 302); + assert.equal(response, originalResponse); }); }); diff --git a/packages/integrations/mdx/package.json b/packages/integrations/mdx/package.json index 24682cfb44bb..587e9dc3a0f2 100644 --- a/packages/integrations/mdx/package.json +++ b/packages/integrations/mdx/package.json @@ -52,6 +52,8 @@ "astro": "^6.0.0-alpha.0" }, "devDependencies": { + "@shikijs/rehype": "^3.22.0", + "@shikijs/twoslash": "^3.22.0", "@types/estree": "^1.0.8", "@types/hast": "^3.0.4", "@types/mdast": "^4.0.4", @@ -65,7 +67,6 @@ "rehype-pretty-code": "^0.14.1", "remark-math": "^6.0.0", "remark-rehype": "^11.1.2", - "remark-shiki-twoslash": "^3.1.3", "remark-toc": "^9.0.0", "shiki": "^3.22.0", "unified": "^11.0.5", diff --git a/packages/integrations/mdx/test/fixtures/mdx-syntax-hightlighting/src/pages/index.mdx b/packages/integrations/mdx/test/fixtures/mdx-syntax-hightlighting/src/pages/index.mdx index 866387e570db..c7275d2b4580 100644 --- a/packages/integrations/mdx/test/fixtures/mdx-syntax-hightlighting/src/pages/index.mdx +++ b/packages/integrations/mdx/test/fixtures/mdx-syntax-hightlighting/src/pages/index.mdx @@ -7,3 +7,7 @@ const handlesAstroSyntax = true

{handlesAstroSyntax}

``` + +```ts twoslash +console.log("Hover over the function to see its documentation") +``` diff --git a/packages/integrations/mdx/test/mdx-syntax-highlighting.test.js b/packages/integrations/mdx/test/mdx-syntax-highlighting.test.js index 81e051657c4d..d0e6a91d0aae 100644 --- a/packages/integrations/mdx/test/mdx-syntax-highlighting.test.js +++ b/packages/integrations/mdx/test/mdx-syntax-highlighting.test.js @@ -1,9 +1,10 @@ import * as assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import mdx from '@astrojs/mdx'; +import rehypeShiki from '@shikijs/rehype'; +import { transformerTwoslash } from '@shikijs/twoslash'; import { parseHTML } from 'linkedom'; import rehypePrettyCode from 'rehype-pretty-code'; -import shikiTwoslash from 'remark-shiki-twoslash'; import { loadFixture } from '../../../astro/test/test-utils.js'; const FIXTURE_ROOT = new URL('./fixtures/mdx-syntax-hightlighting/', import.meta.url); @@ -95,7 +96,7 @@ describe('MDX syntax highlighting', () => { } }); - it('supports custom highlighter - shiki-twoslash', async () => { + it('supports custom highlighter - @shikijs/rehype and @shikijs/twoslash', async () => { const fixture = await loadFixture({ root: FIXTURE_ROOT, markdown: { @@ -103,7 +104,15 @@ describe('MDX syntax highlighting', () => { }, integrations: [ mdx({ - remarkPlugins: [shikiTwoslash.default ?? shikiTwoslash], + rehypePlugins: [ + [ + rehypeShiki, + { + theme: 'vitesse-light', + transformers: [transformerTwoslash({})], + }, + ], + ], }), ], }); @@ -112,8 +121,11 @@ describe('MDX syntax highlighting', () => { const html = await fixture.readFile('/index.html'); const { document } = parseHTML(html); - const twoslashCodeBlock = document.querySelector('pre.shiki'); - assert.notEqual(twoslashCodeBlock, null); + const shikiCodeBlock = document.querySelector('pre.shiki'); + assert.notEqual(shikiCodeBlock, null); + + const twoslashPopup = document.querySelector('div.twoslash-popup-docs'); + assert.notEqual(twoslashPopup, null); }); it('supports custom highlighter - rehype-pretty-code', async () => { diff --git a/packages/integrations/node/src/serve-static.ts b/packages/integrations/node/src/serve-static.ts index 610c1f0e9fb9..917a24db5c6c 100644 --- a/packages/integrations/node/src/serve-static.ts +++ b/packages/integrations/node/src/serve-static.ts @@ -48,7 +48,13 @@ export function createStaticHandler( }); const routeData = app.match(request, true); if (routeData && routeData.prerender) { - const matchedRoute = headersMap.find((header) => header.pathname.includes(pathname)); + // Headers are stored keyed by base-less route paths (e.g. "/one"), so we + // must strip config.base from the incoming URL before matching, just as + // we do for filesystem access above. + const baselessPathname = prependForwardSlash(app.removeBase(urlPath)); + const matchedRoute = headersMap.find((header) => + header.pathname.includes(baselessPathname), + ); if (matchedRoute) { for (const header of matchedRoute.headers) { res.setHeader(header.key, header.value); diff --git a/packages/integrations/node/test/static-headers.test.js b/packages/integrations/node/test/static-headers.test.js index 86c124200879..3dc3d98f4c37 100644 --- a/packages/integrations/node/test/static-headers.test.js +++ b/packages/integrations/node/test/static-headers.test.js @@ -36,11 +36,13 @@ describe('Static headers', () => { before(async () => { fixture = await loadFixture({ root: './fixtures/static-headers/', + outDir: './dist/root-base', output: 'server', adapter: nodejs({ mode: 'standalone', staticHeaders: true }), }); await fixture.build(); const { startServer } = await fixture.loadAdapterEntryModule(); + process.env.PORT = '4322'; const res = startServer(); server = res.server; await waitServerListen(server.server); @@ -69,3 +71,49 @@ describe('Static headers', () => { ); }); }); + +describe('Static headers with non-root base', () => { + /** @type {import('./test-utils').Fixture} */ + let fixture; + let server; + + before(async () => { + fixture = await loadFixture({ + root: './fixtures/static-headers/', + outDir: './dist/non-root-base', + base: '/docs', + output: 'server', + adapter: nodejs({ mode: 'standalone', staticHeaders: true }), + }); + await fixture.build(); + const { startServer } = await fixture.loadAdapterEntryModule(); + process.env.PORT = '4323'; + const res = startServer(); + server = res.server; + await waitServerListen(server.server); + }); + + after(async () => { + await server.stop(); + }); + + it('CSP headers are added to the index route under the base path', async () => { + const res = await fetch(`http://${server.host}:${server.port}/docs/`); + const csp = res.headers.get('Content-Security-Policy'); + assert.ok(csp, 'Content-Security-Policy header must be present for the index route'); + assert.ok( + csp.includes('script-src'), + 'should contain script-src directive due to server island', + ); + }); + + it('CSP headers are added to a dynamic route under the base path', async () => { + const res = await fetch(`http://${server.host}:${server.port}/docs/one`); + const csp = res.headers.get('Content-Security-Policy'); + assert.ok(csp, 'Content-Security-Policy header must be present for dynamic routes'); + assert.ok( + csp.includes('script-src'), + 'should contain script-src directive due to server island', + ); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0dabc60c8050..84d941474ace 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5460,6 +5460,12 @@ importers: specifier: ^6.0.3 version: 6.0.3 devDependencies: + '@shikijs/rehype': + specifier: ^3.22.0 + version: 3.23.0 + '@shikijs/twoslash': + specifier: ^3.22.0 + version: 3.23.0(typescript@5.9.3) '@types/estree': specifier: ^1.0.8 version: 1.0.8 @@ -5499,9 +5505,6 @@ importers: remark-rehype: specifier: ^11.1.2 version: 11.1.2 - remark-shiki-twoslash: - specifier: ^3.1.3 - version: 3.1.3(typescript@5.9.3) remark-toc: specifier: ^9.0.0 version: 9.0.0 @@ -9806,9 +9809,17 @@ packages: '@shikijs/langs@3.23.0': resolution: {integrity: sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==} + '@shikijs/rehype@3.23.0': + resolution: {integrity: sha512-GepKJxXHbXFfAkiZZZ+4V7x71Lw3s0ALYmydUxJRdvpKjSx9FOMSaunv6WRLFBXR6qjYerUq1YZQno+2gLEPwA==} + '@shikijs/themes@3.23.0': resolution: {integrity: sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==} + '@shikijs/twoslash@3.23.0': + resolution: {integrity: sha512-pNaLJWMA3LU7PhT8tm9OQBZ1epy0jmdgeJzntBtr1EVXLbHxGzTj3mnf9vOdcl84l96qnlJXkJ/NGXZYBpXl5g==} + peerDependencies: + typescript: '>=5.5.0' + '@shikijs/types@3.23.0': resolution: {integrity: sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==} @@ -10227,14 +10238,10 @@ packages: resolution: {integrity: sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript/twoslash@3.1.0': - resolution: {integrity: sha512-kTwMUQ8xtAZaC4wb2XuLkPqFVBj2dNBueMQ89NWEuw87k2nLBbuafeG5cob/QEr6YduxIdTVUjix0MtC7mPlmg==} - - '@typescript/vfs@1.3.4': - resolution: {integrity: sha512-RbyJiaAGQPIcAGWFa3jAXSuAexU4BFiDRF1g3hy7LmRqfNpYlTQWGXjcrOaVZjJ8YkkpuwG0FcsYvtWQpd9igQ==} - - '@typescript/vfs@1.3.5': - resolution: {integrity: sha512-pI8Saqjupf9MfLw7w2+og+fmb0fZS0J6vsKXXrp4/PDXEFvntgzXmChCXC/KefZZS0YGS6AT8e0hGAJcTsdJlg==} + '@typescript/vfs@1.6.4': + resolution: {integrity: sha512-PJFXFS4ZJKiJ9Qiuix6Dz/OwEIqHD7Dme1UwZhTK11vR+5dqW2ACbdndWQexBzCx+CPuMe5WBYQWCsFyGlQLlQ==} + peerDependencies: + typescript: '*' '@typespec/ts-http-runtime@0.3.3': resolution: {integrity: sha512-91fp6CAAJSRtH5ja95T1FHSKa8aPW9/Zw6cta81jlZTUw/+Vq8jM/AfF/14h2b71wwR84JUTW/3Y8QPhDAawFA==} @@ -12052,10 +12059,6 @@ packages: fecha@4.2.3: resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} - fenceparser@1.1.1: - resolution: {integrity: sha512-VdkTsK7GWLT0VWMK5S5WTAPn61wJ98WPFwJiRHumhg4ESNUO/tnkU8bzzzc62o6Uk1SVhuZFLnakmDA4SGV7wA==} - engines: {node: '>=12'} - fetch-blob@3.2.0: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} @@ -13103,10 +13106,6 @@ packages: resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} engines: {node: '>=12'} - lz-string@1.5.0: - resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} - hasBin: true - magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -14373,9 +14372,6 @@ packages: resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - regenerator-runtime@0.13.11: - resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} - regex-recursion@6.0.2: resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} @@ -14444,11 +14440,6 @@ packages: remark-rehype@11.1.2: resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} - remark-shiki-twoslash@3.1.3: - resolution: {integrity: sha512-4e8OH3ySOCw5wUbDcPszokOKjKuebOqlP2WlySvC7ITBOq27BiGsFlq+FNWhxppZ+JzhTWah4gQrnMjX3KDbAQ==} - peerDependencies: - typescript: '>3' - remark-smartypants@3.0.2: resolution: {integrity: sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==} engines: {node: '>=16.0.0'} @@ -14677,14 +14668,6 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shiki-twoslash@3.1.2: - resolution: {integrity: sha512-JBcRIIizi+exIA/OUhYkV6jtyeZco0ykCkIRd5sgwIt1Pm4pz+maoaRZpm6SkhPwvif4fCA7xOtJOykhpIV64Q==} - peerDependencies: - typescript: '>3' - - shiki@0.10.1: - resolution: {integrity: sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==} - shiki@3.23.0: resolution: {integrity: sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==} @@ -15152,9 +15135,6 @@ packages: typescript: optional: true - tslib@2.1.0: - resolution: {integrity: sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==} - tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -15204,6 +15184,14 @@ packages: resolution: {integrity: sha512-H+rwSHHPLoyPOSoHdmI1zY0zy0GGj1Dmr7SeJW+nZiWLz2nex8EJ+fkdVabxXFMNEux+aywI4Sae8EqhmnOv4A==} hasBin: true + twoslash-protocol@0.3.6: + resolution: {integrity: sha512-FHGsJ9Q+EsNr5bEbgG3hnbkvEBdW5STgPU824AHUjB4kw0Dn4p8tABT7Ncg1Ie6V0+mDg3Qpy41VafZXcQhWMA==} + + twoslash@0.3.6: + resolution: {integrity: sha512-VuI5OKl+MaUO9UIW3rXKoPgHI3X40ZgB/j12VY6h98Ae1mCBihjPvhOPeJWlxCYcmSbmeZt5ZKkK0dsVtp+6pA==} + peerDependencies: + typescript: ^5.5.0 + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -15316,9 +15304,6 @@ packages: unist-util-is@3.0.0: resolution: {integrity: sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==} - unist-util-is@4.1.0: - resolution: {integrity: sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==} - unist-util-is@6.0.1: resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} @@ -15346,18 +15331,12 @@ packages: unist-util-visit-parents@2.1.2: resolution: {integrity: sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==} - unist-util-visit-parents@3.1.1: - resolution: {integrity: sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==} - unist-util-visit-parents@6.0.2: resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} unist-util-visit@1.4.1: resolution: {integrity: sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==} - unist-util-visit@2.0.3: - resolution: {integrity: sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==} - unist-util-visit@5.1.0: resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} @@ -15840,9 +15819,6 @@ packages: vscode-oniguruma@1.7.0: resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==} - vscode-textmate@5.2.0: - resolution: {integrity: sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==} - vscode-textmate@7.0.4: resolution: {integrity: sha512-9hJp0xL7HW1Q5OgGe03NACo7yiCTMEk3WU/rtKXUbncLtdg6rVVNJnHwD88UhbIYU2KoxY0Dih0x+kIsmUKn2A==} @@ -18921,10 +18897,28 @@ snapshots: dependencies: '@shikijs/types': 3.23.0 + '@shikijs/rehype@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + '@types/hast': 3.0.4 + hast-util-to-string: 3.0.1 + shiki: 3.23.0 + unified: 11.0.5 + unist-util-visit: 5.1.0 + '@shikijs/themes@3.23.0': dependencies: '@shikijs/types': 3.23.0 + '@shikijs/twoslash@3.23.0(typescript@5.9.3)': + dependencies: + '@shikijs/core': 3.23.0 + '@shikijs/types': 3.23.0 + twoslash: 0.3.6(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + '@shikijs/types@3.23.0': dependencies: '@shikijs/vscode-textmate': 10.0.2 @@ -19359,23 +19353,10 @@ snapshots: '@typescript-eslint/types': 8.56.1 eslint-visitor-keys: 5.0.0 - '@typescript/twoslash@3.1.0': - dependencies: - '@typescript/vfs': 1.3.5 - debug: 4.4.3(supports-color@8.1.1) - lz-string: 1.5.0 - transitivePeerDependencies: - - supports-color - - '@typescript/vfs@1.3.4': - dependencies: - debug: 4.4.3(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - - '@typescript/vfs@1.3.5': + '@typescript/vfs@1.6.4(typescript@5.9.3)': dependencies: debug: 4.4.3(supports-color@8.1.1) + typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -21458,8 +21439,6 @@ snapshots: fecha@4.2.3: {} - fenceparser@1.1.1: {} - fetch-blob@3.2.0: dependencies: node-domexception: 1.0.0 @@ -22611,8 +22590,6 @@ snapshots: luxon@3.7.2: {} - lz-string@1.5.0: {} - magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -24282,8 +24259,6 @@ snapshots: dependencies: '@eslint-community/regexpp': 4.12.2 - regenerator-runtime@0.13.11: {} - regex-recursion@6.0.2: dependencies: regex-utilities: 2.3.0 @@ -24424,21 +24399,6 @@ snapshots: unified: 11.0.5 vfile: 6.0.3 - remark-shiki-twoslash@3.1.3(typescript@5.9.3): - dependencies: - '@types/unist': 2.0.11 - '@typescript/twoslash': 3.1.0 - '@typescript/vfs': 1.3.4 - fenceparser: 1.1.1 - regenerator-runtime: 0.13.11 - shiki: 0.10.1 - shiki-twoslash: 3.1.2(typescript@5.9.3) - tslib: 2.1.0 - typescript: 5.9.3 - unist-util-visit: 2.0.3 - transitivePeerDependencies: - - supports-color - remark-smartypants@3.0.2: dependencies: retext: 9.0.0 @@ -24737,22 +24697,6 @@ snapshots: shebang-regex@3.0.0: {} - shiki-twoslash@3.1.2(typescript@5.9.3): - dependencies: - '@typescript/twoslash': 3.1.0 - '@typescript/vfs': 1.3.4 - fenceparser: 1.1.1 - shiki: 0.10.1 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - shiki@0.10.1: - dependencies: - jsonc-parser: 3.3.1 - vscode-oniguruma: 1.7.0 - vscode-textmate: 5.2.0 - shiki@3.23.0: dependencies: '@shikijs/core': 3.23.0 @@ -25249,8 +25193,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - tslib@2.1.0: {} - tslib@2.8.1: {} tsx@4.21.0: @@ -25294,6 +25236,16 @@ snapshots: turbo-windows-64: 2.8.11 turbo-windows-arm64: 2.8.11 + twoslash-protocol@0.3.6: {} + + twoslash@0.3.6(typescript@5.9.3): + dependencies: + '@typescript/vfs': 1.6.4(typescript@5.9.3) + twoslash-protocol: 0.3.6 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 @@ -25403,8 +25355,6 @@ snapshots: unist-util-is@3.0.0: {} - unist-util-is@4.1.0: {} - unist-util-is@6.0.1: dependencies: '@types/unist': 3.0.3 @@ -25446,11 +25396,6 @@ snapshots: dependencies: unist-util-is: 3.0.0 - unist-util-visit-parents@3.1.1: - dependencies: - '@types/unist': 2.0.11 - unist-util-is: 4.1.0 - unist-util-visit-parents@6.0.2: dependencies: '@types/unist': 3.0.3 @@ -25460,12 +25405,6 @@ snapshots: dependencies: unist-util-visit-parents: 2.1.2 - unist-util-visit@2.0.3: - dependencies: - '@types/unist': 2.0.11 - unist-util-is: 4.1.0 - unist-util-visit-parents: 3.1.1 - unist-util-visit@5.1.0: dependencies: '@types/unist': 3.0.3 @@ -25981,8 +25920,6 @@ snapshots: vscode-oniguruma@1.7.0: {} - vscode-textmate@5.2.0: {} - vscode-textmate@7.0.4: {} vscode-tmgrammar-test@0.1.3: