diff --git a/.changeset/all-loops-deny.md b/.changeset/all-loops-deny.md new file mode 100644 index 000000000000..96d0db76dbac --- /dev/null +++ b/.changeset/all-loops-deny.md @@ -0,0 +1,9 @@ +--- +'astro': minor +--- + +Updates the `NodeApp.match()` function in the Adapter API to accept a second, optional parameter to allow adapter authors to add headers to static, prerendered pages. + +`NodeApp.match(request)` currently checks whether there is a route that matches the given `Request`. If there is a prerendered route, the function returns `undefined`, because static routes are already rendered and their headers cannot be updated. + +When the new, optional boolean parameter is passed (e.g. `NodeApp.match(request, true)`), Astro will return the first matched route, even when it's a prerendered route. This allows your adapter to now access static routes and provides the opportunity to set headers for these pages, for example, to implement a Content Security Policy (CSP). diff --git a/.changeset/full-hoops-hear.md b/.changeset/full-hoops-hear.md new file mode 100644 index 000000000000..7535c5e1ab39 --- /dev/null +++ b/.changeset/full-hoops-hear.md @@ -0,0 +1,7 @@ +--- +'@astrojs/netlify': patch +'@astrojs/vercel': patch +--- + +Fixes the internal implementation of the new feature `experimentalStaticHeaders`, where dynamic routes +were mapped to use always the same header. diff --git a/.changeset/purple-spoons-jog.md b/.changeset/purple-spoons-jog.md new file mode 100644 index 000000000000..0e69da0ed12c --- /dev/null +++ b/.changeset/purple-spoons-jog.md @@ -0,0 +1,22 @@ +--- +'@astrojs/node': minor +--- + +Adds support for the [experimental static headers Astro feature](https://docs.astro.build/en/reference/adapter-reference/#experimentalstaticheaders). + +When the feature is enabled via the option `experimentalStaticHeaders`, and [experimental Content Security Policy](https://docs.astro.build/en/reference/experimental-flags/csp/) is enabled, the adapter will generate `Response` headers for static pages, which allows support for CSP directives that are not supported inside a `` tag (e.g. `frame-ancestors`). + +```js +import { defineConfig } from "astro/config"; +import node from "@astrojs/node"; + +export default defineConfig({ + adapter: node({ + mode: "standalone", + experimentalStaticHeaders: true + }), + experimental: { + cps: true + } +}) +``` diff --git a/packages/astro/src/core/app/index.ts b/packages/astro/src/core/app/index.ts index 188d0390edde..2075c2c1c2af 100644 --- a/packages/astro/src/core/app/index.ts +++ b/packages/astro/src/core/app/index.ts @@ -191,7 +191,15 @@ export class App { } } - match(request: Request): RouteData | undefined { + /** + * Given a `Request`, it returns the `RouteData` that matches its `pathname`. By default, prerendered + * routes aren't returned, even if they are matched. + * + * When `allowPrerenderedRoutes` is `true`, the function returns matched prerendered routes too. + * @param request + * @param allowPrerenderedRoutes + */ + match(request: Request, allowPrerenderedRoutes = false): RouteData | undefined { const url = new URL(request.url); // ignore requests matching public assets if (this.#manifest.assets.has(url.pathname)) return undefined; @@ -201,8 +209,14 @@ export class App { } let routeData = matchRoute(decodeURI(pathname), this.#manifestData); + if (!routeData) return undefined; + if (allowPrerenderedRoutes) { + return routeData; + } // missing routes fall-through, pre rendered are handled by static layer - if (!routeData || routeData.prerender) return undefined; + else if (routeData.prerender) { + return undefined; + } return routeData; } diff --git a/packages/astro/src/core/app/node.ts b/packages/astro/src/core/app/node.ts index dc0a1de21343..71fcd0b6eb14 100644 --- a/packages/astro/src/core/app/node.ts +++ b/packages/astro/src/core/app/node.ts @@ -7,7 +7,7 @@ import { deserializeManifest } from './common.js'; import { createOutgoingHttpHeaders } from './createOutgoingHttpHeaders.js'; import type { RenderOptions } from './index.js'; import { App } from './index.js'; -import type { SerializedSSRManifest, SSRManifest } from './types.js'; +import type { NodeAppHeadersJson, SerializedSSRManifest, SSRManifest } from './types.js'; export { apply as applyPolyfills } from '../polyfill.js'; @@ -20,13 +20,19 @@ interface NodeRequest extends IncomingMessage { } export class NodeApp extends App { - match(req: NodeRequest | Request) { + headersMap: NodeAppHeadersJson | undefined = undefined; + + public setHeadersMap(headers: NodeAppHeadersJson) { + this.headersMap = headers; + } + + match(req: NodeRequest | Request, allowPrerenderedRoutes = false) { if (!(req instanceof Request)) { req = NodeApp.createRequest(req, { skipBody: true, }); } - return super.match(req); + return super.match(req, allowPrerenderedRoutes); } render(request: NodeRequest | Request, options?: RenderOptions): Promise; /** diff --git a/packages/astro/src/core/app/types.ts b/packages/astro/src/core/app/types.ts index 2a761045ef13..c736eda5eb4b 100644 --- a/packages/astro/src/core/app/types.ts +++ b/packages/astro/src/core/app/types.ts @@ -139,3 +139,11 @@ export type SerializedSSRManifest = Omit< serverIslandNameMap: [string, string][]; key: string; }; + +export type NodeAppHeadersJson = { + pathname: string; + headers: { + key: string; + value: string; + }[]; +}[]; diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts index 05798adae4f9..806eb5033954 100644 --- a/packages/astro/src/core/build/generate.ts +++ b/packages/astro/src/core/build/generate.ts @@ -21,7 +21,7 @@ import { getServerOutputDirectory } from '../../prerender/utils.js'; import type { AstroSettings, ComponentInstance } from '../../types/astro.js'; import type { GetStaticPathsItem, MiddlewareHandler } from '../../types/public/common.js'; import type { AstroConfig } from '../../types/public/config.js'; -import type { IntegrationResolvedRoute } from '../../types/public/index.js'; +import type { IntegrationResolvedRoute, RouteToHeaders } from '../../types/public/index.js'; import type { RouteData, RouteType, @@ -102,7 +102,7 @@ export async function generatePages(options: StaticBuildOptions, internals: Buil logger.info('SKIP_FORMAT', `\n${bgGreen(black(` ${verb} static routes `))}`); const builtPaths = new Set(); const pagesToGenerate = pipeline.retrieveRoutesToGenerate(); - const routeToHeaders = new Map(); + const routeToHeaders: RouteToHeaders = new Map(); if (ssr) { for (const [pageData, filePath] of pagesToGenerate) { if (pageData.route.prerender) { @@ -234,7 +234,7 @@ async function generatePage( ssrEntry: SinglePageBuiltModule, builtPaths: Set, pipeline: BuildPipeline, - routeToHeaders: Map, + routeToHeaders: RouteToHeaders, ) { // prepare information we need const { config, logger } = pipeline; @@ -264,6 +264,7 @@ async function generatePage( async function generatePathWithLogs( path: string, route: RouteData, + integrationRoute: IntegrationResolvedRoute, index: number, paths: string[], isConcurrent: boolean, @@ -281,7 +282,14 @@ async function generatePage( logger.info(null, ` ${blue(lineIcon)} ${dim(filePath)}`, false); } - const created = await generatePath(path, pipeline, generationOptions, route, routeToHeaders); + const created = await generatePath( + path, + pipeline, + generationOptions, + route, + integrationRoute, + routeToHeaders, + ); const timeEnd = performance.now(); const isSlow = timeEnd - timeStart > THRESHOLD_SLOW_RENDER_TIME_MS; @@ -298,6 +306,7 @@ async function generatePage( // Now we explode the routes. A route render itself, and it can render its fallbacks (i18n routing) for (const route of eachRouteInRouteData(pageData)) { + const integrationRoute = toIntegrationResolvedRoute(route); const icon = route.type === 'page' || route.type === 'redirect' || route.type === 'fallback' ? green('▶') @@ -313,13 +322,15 @@ async function generatePage( const promises: Promise[] = []; for (let i = 0; i < paths.length; i++) { const path = paths[i]; - promises.push(limit(() => generatePathWithLogs(path, route, i, paths, true))); + promises.push( + limit(() => generatePathWithLogs(path, route, integrationRoute, i, paths, true)), + ); } await Promise.all(promises); } else { for (let i = 0; i < paths.length; i++) { const path = paths[i]; - await generatePathWithLogs(path, route, i, paths, false); + await generatePathWithLogs(path, route, integrationRoute, i, paths, false); } } } @@ -499,7 +510,8 @@ async function generatePath( pipeline: BuildPipeline, gopts: GeneratePathOptions, route: RouteData, - routeToHeaders: Map, + integrationRoute: IntegrationResolvedRoute, + routeToHeaders: RouteToHeaders, ): Promise { const { mod } = gopts; const { config, logger, options } = pipeline; @@ -569,20 +581,14 @@ async function generatePath( throw err; } - if ( - pipeline.settings.adapter?.adapterFeatures?.experimentalStaticHeaders && - pipeline.settings.config.experimental?.csp - ) { - routeToHeaders.set(toIntegrationResolvedRoute(route), response.headers); - } - + const responseHeaders = response.headers; if (response.status >= 300 && response.status < 400) { // Adapters may handle redirects themselves, turning off Astro's redirect handling using `config.build.redirects` in the process. // In that case, we skip rendering static files for the redirect routes. if (routeIsRedirect(route) && !config.build.redirects) { return undefined; } - const locationSite = getRedirectLocationOrThrow(response.headers); + const locationSite = getRedirectLocationOrThrow(responseHeaders); const siteURL = config.site; const location = siteURL ? new URL(locationSite, siteURL) : locationSite; const fromPath = new URL(request.url).pathname; @@ -616,6 +622,13 @@ async function generatePath( route.distURL = [outFile]; } + if ( + pipeline.settings.adapter?.adapterFeatures?.experimentalStaticHeaders && + pipeline.settings.config.experimental?.csp + ) { + routeToHeaders.set(pathname, { headers: responseHeaders, route: integrationRoute }); + } + await fs.promises.mkdir(outFolder, { recursive: true }); await fs.promises.writeFile(outFile, body); diff --git a/packages/astro/src/core/build/plugins/plugin-manifest.ts b/packages/astro/src/core/build/plugins/plugin-manifest.ts index fdd1ea2c4b7a..4cd4c2f3c62e 100644 --- a/packages/astro/src/core/build/plugins/plugin-manifest.ts +++ b/packages/astro/src/core/build/plugins/plugin-manifest.ts @@ -244,9 +244,15 @@ async function buildManifest( staticFiles.push(file); } + const needsStaticHeaders = settings.adapter?.adapterFeatures?.experimentalStaticHeaders ?? false; + for (const route of opts.routesList.routes) { const pageData = internals.pagesByKeys.get(makePageDataKey(route.route, route.component)); - if (route.prerender || !pageData) continue; + if (!pageData) continue; + + if (route.prerender && !needsStaticHeaders) { + continue; + } const scripts: SerializedRouteInfo['scripts'] = []; if (settings.scripts.some((script) => script.stage === 'page')) { const src = entryModules[PAGE_SCRIPT_ID]; diff --git a/packages/astro/src/integrations/hooks.ts b/packages/astro/src/integrations/hooks.ts index 0ff4225ee33a..7e28a782d7d6 100644 --- a/packages/astro/src/integrations/hooks.ts +++ b/packages/astro/src/integrations/hooks.ts @@ -32,6 +32,7 @@ import type { IntegrationResolvedRoute, IntegrationRouteData, RouteOptions, + RouteToHeaders, } from '../types/public/integrations.js'; import type { RouteData } from '../types/public/internal.js'; import { validateSupportedFeatures } from './features-validation.js'; @@ -590,7 +591,7 @@ export async function runHookBuildGenerated({ }: { settings: AstroSettings; logger: Logger; - experimentalRouteToHeaders: Map; + experimentalRouteToHeaders: RouteToHeaders; }) { const dir = settings.buildOutput === 'server' ? settings.config.build.client : settings.config.outDir; diff --git a/packages/astro/src/types/public/index.ts b/packages/astro/src/types/public/index.ts index e473ab0f96fe..42c131fc2b68 100644 --- a/packages/astro/src/types/public/index.ts +++ b/packages/astro/src/types/public/index.ts @@ -21,7 +21,7 @@ export type { UnresolvedImageTransform, } from '../../assets/types.js'; export type { ContainerRenderer } from '../../container/index.js'; -export type { AssetsPrefix, SSRManifest } from '../../core/app/types.js'; +export type { AssetsPrefix, NodeAppHeadersJson, SSRManifest } from '../../core/app/types.js'; export type { AstroCookieGetOptions, AstroCookieSetOptions, diff --git a/packages/astro/src/types/public/integrations.ts b/packages/astro/src/types/public/integrations.ts index 1ef37af44a5d..da08c0bd913f 100644 --- a/packages/astro/src/types/public/integrations.ts +++ b/packages/astro/src/types/public/integrations.ts @@ -234,7 +234,7 @@ export interface BaseIntegrationHooks { 'astro:build:generated': (options: { dir: URL; logger: AstroIntegrationLogger; - experimentalRouteToHeaders: Map; + experimentalRouteToHeaders: RouteToHeaders; }) => void | Promise; 'astro:build:done': (options: { pages: { pathname: string }[]; @@ -277,6 +277,13 @@ export type IntegrationRouteData = Omit< redirectRoute?: IntegrationRouteData; }; +export type RouteToHeaders = Map; + +export type HeaderPayload = { + headers: Headers; + route: IntegrationResolvedRoute; +}; + export interface IntegrationResolvedRoute extends Pick< RouteData, diff --git a/packages/astro/test/csp.test.js b/packages/astro/test/csp.test.js index 13e0810ed88c..7efe59102bbf 100644 --- a/packages/astro/test/csp.test.js +++ b/packages/astro/test/csp.test.js @@ -321,13 +321,14 @@ describe('CSP', () => { await fixture.build(); app = await fixture.loadTestAdapterApp(); - assert.equal( - routeToHeaders.size, - 4, - 'expected four routes: /, /scripts, /title/foo, /title/bar', - ); + assert.equal(routeToHeaders.size, 4, 'expected four routes: /, /scripts, /foo, /bar'); + + assert.ok(routeToHeaders.has('/'), 'should have a CSP header for /'); + assert.ok(routeToHeaders.has('/scripts'), 'should have a CSP header for /scripts'); + assert.ok(routeToHeaders.has('/foo'), 'should have a CSP header for /foo'); + assert.ok(routeToHeaders.has('/bar'), 'should have a CSP header for /bar'); - for (const headers of routeToHeaders.values()) { + for (const { headers } of routeToHeaders.values()) { assert.ok(headers.has('content-security-policy'), 'should have a CSP header'); } }); diff --git a/packages/astro/test/fixtures/csp-adapter/src/pages/[title].astro b/packages/astro/test/fixtures/csp-adapter/src/pages/[title].astro index fe289397a4fe..07ce2ece662d 100644 --- a/packages/astro/test/fixtures/csp-adapter/src/pages/[title].astro +++ b/packages/astro/test/fixtures/csp-adapter/src/pages/[title].astro @@ -5,8 +5,8 @@ export const prerender = true export const getStaticPaths = (() => { return [ - { params: { title: 'Foo' } }, - { params: { title: 'Bar' } }, + { params: { title: 'foo' } }, + { params: { title: 'bar' } }, ]; }); diff --git a/packages/integrations/netlify/src/index.ts b/packages/integrations/netlify/src/index.ts index 0ed91a8ddbb3..67b47aba1d9d 100644 --- a/packages/integrations/netlify/src/index.ts +++ b/packages/integrations/netlify/src/index.ts @@ -3,11 +3,7 @@ import { appendFile, mkdir, readFile, writeFile } from 'node:fs/promises'; import type { IncomingMessage } from 'node:http'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { emptyDir } from '@astrojs/internal-helpers/fs'; -import { - createHostedRouteDefinition, - createRedirectsFromAstroRoutes, - printAsRedirects, -} from '@astrojs/underscore-redirects'; +import { createRedirectsFromAstroRoutes, printAsRedirects } from '@astrojs/underscore-redirects'; import type { Context } from '@netlify/functions'; import type { AstroConfig, @@ -15,6 +11,7 @@ import type { AstroIntegrationLogger, HookParameters, IntegrationResolvedRoute, + RouteToHeaders, } from 'astro'; import { build } from 'esbuild'; import { glob, globSync } from 'tinyglobby'; @@ -109,7 +106,7 @@ export function remotePatternToRegex( async function writeNetlifyFrameworkConfig( config: AstroConfig, - staticHeaders: Map | undefined, + staticHeaders: RouteToHeaders | undefined, logger: AstroIntegrationLogger, ) { const remoteImages: Array = []; @@ -135,22 +132,13 @@ async function writeNetlifyFrameworkConfig( } if (staticHeaders && staticHeaders.size > 0) { - for (const [route, routeHeaders] of staticHeaders.entries()) { - if (!route.isPrerendered) { - continue; - } - if (route.redirect) { - continue; - } - - const definition = createHostedRouteDefinition(route, config); - + for (const [pathname, { headers: routeHeaders }] of staticHeaders.entries()) { if (config.experimental.csp) { const csp = routeHeaders.get('Content-Security-Policy'); if (csp) { headers.push({ - for: definition.input, + for: pathname, values: { 'Content-Security-Policy': csp, }, @@ -259,7 +247,7 @@ export default function netlifyIntegration( let outDir: URL; let rootDir: URL; let astroMiddlewareEntryPoint: URL | undefined = undefined; - let staticHeadersMap: Map | undefined = undefined; + let staticHeadersMap: RouteToHeaders | undefined = undefined; // Extra files to be merged with `includeFiles` during build const extraFilesToInclude: URL[] = []; // Secret used to verify that the caller is the astro-generated edge middleware and not a third-party diff --git a/packages/integrations/node/package.json b/packages/integrations/node/package.json index 37583ac453ab..2020307fd983 100644 --- a/packages/integrations/node/package.json +++ b/packages/integrations/node/package.json @@ -27,6 +27,7 @@ "dist" ], "scripts": { + "dev": "astro-scripts dev \"src/**/*.ts\"", "build": "astro-scripts build \"src/**/*.ts\" && tsc", "build:ci": "astro-scripts build \"src/**/*.ts\"", "test": "astro-scripts test \"test/**/*.test.js\"" diff --git a/packages/integrations/node/src/index.ts b/packages/integrations/node/src/index.ts index 26ffd2ad05c5..68ea4b08d06e 100644 --- a/packages/integrations/node/src/index.ts +++ b/packages/integrations/node/src/index.ts @@ -1,6 +1,8 @@ import { fileURLToPath } from 'node:url'; -import type { AstroAdapter, AstroIntegration } from 'astro'; +import { writeJson } from '@astrojs/internal-helpers/fs'; +import type { AstroAdapter, AstroConfig, AstroIntegration, NodeAppHeadersJson, RouteToHeaders } from 'astro'; import { AstroError } from 'astro/errors'; +import { STATIC_HEADERS_FILE } from './shared.js'; import type { Options, UserOptions } from './types.js'; export function getAdapter(options: Options): AstroAdapter { @@ -13,6 +15,7 @@ export function getAdapter(options: Options): AstroAdapter { adapterFeatures: { buildOutput: 'server', edgeMiddleware: false, + experimentalStaticHeaders: options.experimentalStaticHeaders, }, supportedAstroFeatures: { hybridOutput: 'stable', @@ -31,12 +34,14 @@ export default function createIntegration(userOptions: UserOptions): AstroIntegr } let _options: Options; + let _config: AstroConfig | undefined = undefined; + let _routeToHeaders: RouteToHeaders | undefined = undefined; return { name: '@astrojs/node', hooks: { 'astro:config:setup': async ({ updateConfig, config, logger }) => { let session = config.session; - + _config = config; if (!session?.driver) { logger.info('Enabling sessions with filesystem storage'); session = { @@ -63,6 +68,9 @@ export default function createIntegration(userOptions: UserOptions): AstroIntegr }, }); }, + 'astro:build:generated': ({ experimentalRouteToHeaders }) => { + _routeToHeaders = experimentalRouteToHeaders; + }, 'astro:config:done': ({ setAdapter, config }) => { _options = { ...userOptions, @@ -71,9 +79,39 @@ export default function createIntegration(userOptions: UserOptions): AstroIntegr host: config.server.host, port: config.server.port, assets: config.build.assets, + experimentalStaticHeaders: userOptions.experimentalStaticHeaders ?? false, }; setAdapter(getAdapter(_options)); }, + 'astro:build:done': async () => { + if (!_config) { + return; + } + + if (_routeToHeaders && _routeToHeaders.size > 0) { + const headersFileUrl = new URL(STATIC_HEADERS_FILE, _config.outDir); + const headersValue: NodeAppHeadersJson = []; + + for (const [pathname, { headers }] of _routeToHeaders.entries()) { + if (_config.experimental.csp) { + const csp = headers.get('Content-Security-Policy'); + if (csp) { + headersValue.push({ + pathname, + headers: [ + { + key: 'Content-Security-Policy', + value: csp, + }, + ], + }); + } + } + } + + await writeJson(headersFileUrl, headersValue); + } + }, }, }; } diff --git a/packages/integrations/node/src/serve-static.ts b/packages/integrations/node/src/serve-static.ts index f26a2ff1f8f3..8114552bb024 100644 --- a/packages/integrations/node/src/serve-static.ts +++ b/packages/integrations/node/src/serve-static.ts @@ -33,6 +33,18 @@ export function createStaticHandler(app: NodeApp, options: Options) { const hasSlash = urlPath.endsWith('/'); let pathname = urlPath; + if (app.headersMap && app.headersMap.length > 0) { + const routeData = app.match(req, true); + if (routeData && routeData.prerender) { + const matchedRoute = app.headersMap.find((header) => header.pathname.includes(pathname)); + if (matchedRoute) { + for (const header of matchedRoute.headers) { + res.setHeader(header.key, header.value); + } + } + } + } + switch (trailingSlash) { case 'never': { if (isDirectory && urlPath !== '/' && hasSlash) { diff --git a/packages/integrations/node/src/server.ts b/packages/integrations/node/src/server.ts index af27ec36dde9..0ed42cb3bb01 100644 --- a/packages/integrations/node/src/server.ts +++ b/packages/integrations/node/src/server.ts @@ -1,10 +1,12 @@ // Keep at the top import './polyfill.js'; -import type { SSRManifest } from 'astro'; +import { existsSync, readFileSync } from 'node:fs'; +import type { NodeAppHeadersJson, SSRManifest } from 'astro'; import { NodeApp } from 'astro/app/node'; import { setGetEnv } from 'astro/env/setup'; import createMiddleware from './middleware.js'; +import { STATIC_HEADERS_FILE } from './shared.js'; import startServer, { createStandaloneHandler } from './standalone.js'; import type { Options } from './types.js'; @@ -12,6 +14,14 @@ setGetEnv((key) => process.env[key]); export function createExports(manifest: SSRManifest, options: Options) { const app = new NodeApp(manifest); + let headersMap: NodeAppHeadersJson | undefined = undefined; + if (options.experimentalStaticHeaders) { + headersMap = readHeadersJson(manifest.outDir); + } + + if (headersMap) { + app.setHeadersMap(headersMap); + } options.trailingSlash = manifest.trailingSlash; return { options: options, @@ -26,6 +36,30 @@ export function start(manifest: SSRManifest, options: Options) { return; } + let headersMap: NodeAppHeadersJson | undefined = undefined; + if (options.experimentalStaticHeaders) { + headersMap = readHeadersJson(manifest.outDir); + } + const app = new NodeApp(manifest); + if (headersMap) { + app.setHeadersMap(headersMap); + } startServer(app, options); } + +function readHeadersJson(outDir: string | URL): NodeAppHeadersJson | undefined { + let headersMap: NodeAppHeadersJson | undefined = undefined; + + const headersUrl = new URL(STATIC_HEADERS_FILE, outDir); + if (existsSync(headersUrl)) { + const content = readFileSync(headersUrl, 'utf-8'); + try { + headersMap = JSON.parse(content) as NodeAppHeadersJson; + } catch (e: any) { + console.error('[@astrojs/node] Error parsing _headers.json: ' + e.message); + console.error('[@astrojs/node] Please make sure your _headers.json is valid JSON.'); + } + } + return headersMap; +} diff --git a/packages/integrations/node/src/shared.ts b/packages/integrations/node/src/shared.ts new file mode 100644 index 000000000000..499fa92b9ef6 --- /dev/null +++ b/packages/integrations/node/src/shared.ts @@ -0,0 +1 @@ +export const STATIC_HEADERS_FILE = "_experimentalHeaders.json" diff --git a/packages/integrations/node/src/types.ts b/packages/integrations/node/src/types.ts index 37773e24cad8..5cf225270347 100644 --- a/packages/integrations/node/src/types.ts +++ b/packages/integrations/node/src/types.ts @@ -9,6 +9,14 @@ export interface UserOptions { * - 'standalone' - Build to a standalone server. The server starts up just by running the built script. */ mode: 'middleware' | 'standalone'; + + /** + * If enabled, the adapter will save [static headers in the framework API file](https://docs.netlify.com/frameworks-api/#headers). + * + * Here the list of the headers that are added: + * - The CSP header of the static pages is added when CSP support is enabled. + */ + experimentalStaticHeaders?: boolean; } export interface Options extends UserOptions { @@ -18,6 +26,7 @@ export interface Options extends UserOptions { client: string; assets: string; trailingSlash?: SSRManifest['trailingSlash']; + experimentalStaticHeaders: boolean; } export type RequestHandler = (...args: RequestHandlerParams) => void | Promise; diff --git a/packages/integrations/node/test/fixtures/sessions/src/middleware.ts b/packages/integrations/node/test/fixtures/sessions/src/middleware.ts index 7f56f11f364f..8b4458961e4a 100644 --- a/packages/integrations/node/test/fixtures/sessions/src/middleware.ts +++ b/packages/integrations/node/test/fixtures/sessions/src/middleware.ts @@ -10,8 +10,6 @@ export const onRequest = defineMiddleware(async (context, next) => { const { action, setActionResult, serializeActionResult } = getActionContext(context); - console.log(action?.name) - const actionPayload = await context.session.get(ACTION_SESSION_KEY); if (actionPayload) { diff --git a/packages/integrations/node/test/fixtures/static-headers/astro.config.mjs b/packages/integrations/node/test/fixtures/static-headers/astro.config.mjs new file mode 100644 index 000000000000..552699376862 --- /dev/null +++ b/packages/integrations/node/test/fixtures/static-headers/astro.config.mjs @@ -0,0 +1,12 @@ +import node from '@astrojs/node' + +export default { + output: 'server', + adapter: node({ + mode: 'standalone', + experimentalStaticHeaders: true + }), + experimental: { + csp: true + } +}; diff --git a/packages/integrations/node/test/fixtures/static-headers/package.json b/packages/integrations/node/test/fixtures/static-headers/package.json new file mode 100644 index 000000000000..aa36f5296fd1 --- /dev/null +++ b/packages/integrations/node/test/fixtures/static-headers/package.json @@ -0,0 +1,9 @@ +{ + "name": "@test/node-static-headers", + "version": "0.0.0", + "private": true, + "dependencies": { + "@astrojs/node": "workspace:*", + "astro": "workspace:*" + } +} diff --git a/packages/integrations/node/test/fixtures/static-headers/src/components/Island.astro b/packages/integrations/node/test/fixtures/static-headers/src/components/Island.astro new file mode 100644 index 000000000000..305caf85a9e1 --- /dev/null +++ b/packages/integrations/node/test/fixtures/static-headers/src/components/Island.astro @@ -0,0 +1 @@ +

I am a server island

diff --git a/packages/integrations/node/test/fixtures/static-headers/src/pages/[slug].astro b/packages/integrations/node/test/fixtures/static-headers/src/pages/[slug].astro new file mode 100644 index 000000000000..c2f3faae327a --- /dev/null +++ b/packages/integrations/node/test/fixtures/static-headers/src/pages/[slug].astro @@ -0,0 +1,18 @@ +--- +import type { GetStaticPaths } from "astro"; +export const getStaticPaths = (() => { + return [ + { params: { slug: "one" } }, + { params: { slug: "two" } }, + { params: { slug: "three" }}, + ]; +}) satisfies GetStaticPaths; + +export const prerender = true; +--- + +{Astro.params.slug} + +

Index

+ + diff --git a/packages/integrations/node/test/fixtures/static-headers/src/pages/index.astro b/packages/integrations/node/test/fixtures/static-headers/src/pages/index.astro new file mode 100644 index 000000000000..7937887b0396 --- /dev/null +++ b/packages/integrations/node/test/fixtures/static-headers/src/pages/index.astro @@ -0,0 +1,11 @@ +--- +import Island from "../components/Island.astro" +export const prerender = true; +--- + +Index + +

Index

+ + + diff --git a/packages/integrations/node/test/static-headers.test.js b/packages/integrations/node/test/static-headers.test.js new file mode 100644 index 000000000000..626064c2abee --- /dev/null +++ b/packages/integrations/node/test/static-headers.test.js @@ -0,0 +1,71 @@ +import * as assert from 'node:assert/strict'; +import { after, before, describe, it } from 'node:test'; +import nodejs from '../dist/index.js'; +import { loadFixture, waitServerListen } from './test-utils.js'; + +describe('Static headers', () => { + let fixture; + + before(async () => { + fixture = await loadFixture({ root: './fixtures/static-headers' }); + await fixture.build(); + }); + + it('CSP headers are added when CSP is enabled', async () => { + const headers = JSON.parse(await fixture.readFile('../dist/_experimentalHeaders.json')); + + const csp = headers + .find((x) => x.pathname === '/') + .headers.find((x) => x.key === 'Content-Security-Policy'); + + assert.notEqual(csp, undefined, 'the index must have CSP headers'); + assert.ok( + csp.value.includes('script-src'), + 'must contain the script-src directive because of the server island', + ); + }); + + it('CSP headers are added to the request', async () => {}); +}); + +describe('Static headers', () => { + /** @type {import('./test-utils').Fixture} */ + let fixture; + let server; + + before(async () => { + fixture = await loadFixture({ + root: './fixtures/static-headers/', + output: 'server', + adapter: nodejs({ mode: 'standalone', experimentalStaticHeaders: true }), + }); + await fixture.build(); + const { startServer } = await fixture.loadAdapterEntryModule(); + const res = startServer(); + server = res.server; + await waitServerListen(server.server); + }); + + after(async () => { + await server.stop(); + // await fixture.clean(); + }); + + it('CSP headers are added to the request', async () => { + const res = await fetch(`http://${server.host}:${server.port}/`); + const cps = res.headers.get('Content-Security-Policy'); + assert.ok( + cps.includes('script-src'), + 'should contain script-src directive due to server island', + ); + }); + + it('CSP headers are added to dynamic orute', async () => { + const res = await fetch(`http://${server.host}:${server.port}/one`); + const cps = res.headers.get('Content-Security-Policy'); + assert.ok( + cps.includes('script-src'), + 'should contain script-src directive due to server island', + ); + }); +}); diff --git a/packages/integrations/vercel/package.json b/packages/integrations/vercel/package.json index 59dd196a6f12..d7e8d22abf0d 100644 --- a/packages/integrations/vercel/package.json +++ b/packages/integrations/vercel/package.json @@ -54,8 +54,7 @@ "@vercel/nft": "^0.29.2", "@vercel/routing-utils": "^5.0.4", "esbuild": "^0.25.0", - "tinyglobby": "^0.2.13", - "@astrojs/underscore-redirects": "workspace:*" + "tinyglobby": "^0.2.13" }, "peerDependencies": { "astro": "^5.0.0" diff --git a/packages/integrations/vercel/src/index.ts b/packages/integrations/vercel/src/index.ts index 237c56221a7f..a1e889589f72 100644 --- a/packages/integrations/vercel/src/index.ts +++ b/packages/integrations/vercel/src/index.ts @@ -2,7 +2,6 @@ import { cpSync, existsSync, mkdirSync, readFileSync } from 'node:fs'; import { basename } from 'node:path'; import { pathToFileURL } from 'node:url'; import { emptyDir, removeDir, writeJson } from '@astrojs/internal-helpers/fs'; -import { createHostedRouteDefinition } from '@astrojs/underscore-redirects'; import { getTransformedRoutes, type Header, @@ -16,6 +15,7 @@ import type { AstroIntegrationLogger, HookParameters, IntegrationResolvedRoute, + RouteToHeaders, } from 'astro'; import { AstroError } from 'astro/errors'; import { globSync } from 'tinyglobby'; @@ -204,7 +204,7 @@ export default function vercelAdapter({ let _serverEntry: string; let _entryPoints: Map, URL>; let _middlewareEntryPoint: URL | undefined; - let _routeToHeaders: Map | undefined = undefined; + let _routeToHeaders: RouteToHeaders | undefined = undefined; // Extra files to be merged with `includeFiles` during build const extraFilesToInclude: URL[] = []; // Secret used to verify that the caller is the astro-generated edge middleware and not a third-party @@ -739,27 +739,15 @@ function getRuntime(process: NodeJS.Process, logger: AstroIntegrationLogger): Ru return 'nodejs18.x'; } -function createConfigHeaders( - staticHeaders: Map, - config: AstroConfig, -): Header[] { +function createConfigHeaders(staticHeaders: RouteToHeaders, config: AstroConfig): Header[] { const vercelHeaders: Header[] = []; - for (const [route, routeHeaders] of staticHeaders.entries()) { - if (!route.isPrerendered) { - continue; - } - if (route.redirect) { - continue; - } - - const definition = createHostedRouteDefinition(route, config); - + for (const [pathname, { headers }] of staticHeaders.entries()) { if (config.experimental.csp) { - const csp = routeHeaders.get('Content-Security-Policy'); + const csp = headers.get('Content-Security-Policy'); if (csp) { vercelHeaders.push({ - source: definition.input, + source: pathname, headers: [ { key: 'Content-Security-Policy', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f27fbcafad3d..b722e7dbcea5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -252,7 +252,7 @@ importers: version: 1.9.7 svelte: specifier: ^5.25.7 - version: 5.34.7 + version: 5.34.9 vue: specifier: ^3.5.13 version: 3.5.17(typescript@5.8.3) @@ -315,7 +315,7 @@ importers: version: link:../../packages/astro svelte: specifier: ^5.25.7 - version: 5.34.7 + version: 5.34.9 examples/framework-vue: dependencies: @@ -369,7 +369,7 @@ importers: version: link:../../packages/astro svelte: specifier: ^5.25.7 - version: 5.34.7 + version: 5.34.9 examples/starlog: dependencies: @@ -751,7 +751,7 @@ importers: version: 5.8.3 undici: specifier: ^7.5.0 - version: 7.10.0 + version: 7.11.0 unified: specifier: ^11.0.5 version: 11.0.5 @@ -929,7 +929,7 @@ importers: version: 1.9.7 svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 vue: specifier: ^3.5.13 version: 3.5.17(typescript@5.8.3) @@ -1074,7 +1074,7 @@ importers: version: 1.9.7 svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 vue: specifier: ^3.5.13 version: 3.5.17(typescript@5.8.3) @@ -1131,7 +1131,7 @@ importers: version: 1.9.7 svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 vue: specifier: ^3.5.13 version: 3.5.17(typescript@5.8.3) @@ -1187,7 +1187,7 @@ importers: version: 1.9.7 svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 vue: specifier: ^3.5.13 version: 3.5.17(typescript@5.8.3) @@ -1227,7 +1227,7 @@ importers: version: 1.9.7 svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 vue: specifier: ^3.5.13 version: 3.5.17(typescript@5.8.3) @@ -1267,7 +1267,7 @@ importers: version: 1.9.7 svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 vue: specifier: ^3.5.13 version: 3.5.17(typescript@5.8.3) @@ -1307,7 +1307,7 @@ importers: version: 1.9.7 svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 vue: specifier: ^3.5.13 version: 3.5.17(typescript@5.8.3) @@ -1347,7 +1347,7 @@ importers: version: 1.9.7 svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 vue: specifier: ^3.5.13 version: 3.5.17(typescript@5.8.3) @@ -1387,7 +1387,7 @@ importers: version: 1.9.7 svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 vue: specifier: ^3.5.13 version: 3.5.17(typescript@5.8.3) @@ -1583,7 +1583,7 @@ importers: version: link:../../.. svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 packages/astro/e2e/fixtures/tailwindcss: dependencies: @@ -1643,7 +1643,7 @@ importers: version: 1.9.7 svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 vue: specifier: ^3.5.13 version: 3.5.17(typescript@5.8.3) @@ -1787,7 +1787,7 @@ importers: version: 18.3.1(react@18.3.1) svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 vue: specifier: ^3.5.13 version: 3.5.17(typescript@5.8.3) @@ -1808,7 +1808,7 @@ importers: version: link:../../.. svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 packages/astro/test/fixtures/alias-tsconfig: dependencies: @@ -1823,7 +1823,7 @@ importers: version: link:../../.. svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 packages/astro/test/fixtures/alias-tsconfig-baseurl-only: dependencies: @@ -1835,7 +1835,7 @@ importers: version: link:../../.. svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 packages/astro/test/fixtures/alias-tsconfig/deps/namespace-package: {} @@ -1960,7 +1960,7 @@ importers: version: 10.26.9 svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 vue: specifier: ^3.5.13 version: 3.5.17(typescript@5.8.3) @@ -1993,7 +1993,7 @@ importers: version: 18.3.1(react@18.3.1) svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 packages/astro/test/fixtures/astro-client-only/pkg: {} @@ -2085,7 +2085,7 @@ importers: version: 18.3.1(react@18.3.1) svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 packages/astro/test/fixtures/astro-env: dependencies: @@ -2433,7 +2433,7 @@ importers: version: 1.9.7 svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 vue: specifier: ^3.5.13 version: 3.5.17(typescript@5.8.3) @@ -2523,7 +2523,7 @@ importers: version: 18.3.1(react@18.3.1) svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 packages/astro/test/fixtures/component-library-shared: dependencies: @@ -2880,7 +2880,7 @@ importers: version: link:../../.. svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 packages/astro/test/fixtures/css-import-as-inline: dependencies: @@ -3162,7 +3162,7 @@ importers: version: 10.26.9 svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 vue: specifier: ^3.5.13 version: 3.5.17(typescript@5.8.3) @@ -3384,7 +3384,7 @@ importers: version: 1.9.7 svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 vue: specifier: ^3.5.13 version: 3.5.17(typescript@5.8.3) @@ -3596,7 +3596,7 @@ importers: version: 1.9.7 svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 vue: specifier: ^3.5.13 version: 3.5.17(typescript@5.8.3) @@ -3814,7 +3814,7 @@ importers: version: link:../../../.. svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 packages/astro/test/fixtures/server-islands/ssr: dependencies: @@ -3829,7 +3829,7 @@ importers: version: link:../../../.. svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 packages/astro/test/fixtures/sessions: dependencies: @@ -3904,7 +3904,7 @@ importers: version: link:../../.. svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 packages/astro/test/fixtures/slots-vue: dependencies: @@ -4241,7 +4241,7 @@ importers: version: link:../../.. svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 packages/astro/test/fixtures/tailwindcss: dependencies: @@ -4352,7 +4352,7 @@ importers: version: link:../../.. svelte: specifier: ^5.25.3 - version: 5.34.7 + version: 5.34.9 vue: specifier: ^3.5.13 version: 3.5.17(typescript@5.8.3) @@ -4418,7 +4418,7 @@ importers: version: 1.0.2 drizzle-orm: specifier: ^0.42.0 - version: 0.42.0(@cloudflare/workers-types@4.20250607.0)(@libsql/client@0.15.2) + version: 0.42.0(@cloudflare/workers-types@4.20250507.0)(@libsql/client@0.15.2) github-slugger: specifier: ^2.0.0 version: 2.0.0 @@ -4680,7 +4680,7 @@ importers: version: link:../../underscore-redirects '@cloudflare/workers-types': specifier: ^4.20250507.0 - version: 4.20250607.0 + version: 4.20250507.0 tinyglobby: specifier: ^0.2.13 version: 0.2.13 @@ -4689,7 +4689,7 @@ importers: version: 6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1) wrangler: specifier: ^4.14.1 - version: 4.14.1(@cloudflare/workers-types@4.20250607.0) + version: 4.14.1(@cloudflare/workers-types@4.20250507.0) devDependencies: astro: specifier: workspace:* @@ -4721,7 +4721,7 @@ importers: devDependencies: wrangler: specifier: ^4.14.1 - version: 4.14.1(@cloudflare/workers-types@4.20250607.0) + version: 4.14.1(@cloudflare/workers-types@4.20250507.0) packages/integrations/cloudflare/test/fixtures/astro-env: dependencies: @@ -4734,7 +4734,7 @@ importers: devDependencies: wrangler: specifier: ^4.14.1 - version: 4.14.1(@cloudflare/workers-types@4.20250607.0) + version: 4.14.1(@cloudflare/workers-types@4.20250507.0) packages/integrations/cloudflare/test/fixtures/compile-image-service: dependencies: @@ -4801,7 +4801,7 @@ importers: version: link:../../../../../astro wrangler: specifier: ^4.14.1 - version: 4.14.1(@cloudflare/workers-types@4.20250607.0) + version: 4.14.1(@cloudflare/workers-types@4.20250507.0) packages/integrations/cloudflare/test/fixtures/with-base: dependencies: @@ -4840,7 +4840,7 @@ importers: version: link:../../../../../astro svelte: specifier: ^5.28.2 - version: 5.34.7 + version: 5.34.9 packages/integrations/cloudflare/test/fixtures/with-vue: dependencies: @@ -5567,6 +5567,15 @@ importers: specifier: workspace:* version: link:../../../../../astro + packages/integrations/node/test/fixtures/static-headers: + dependencies: + '@astrojs/node': + specifier: workspace:* + version: link:../../.. + astro: + specifier: workspace:* + version: link:../../../../../astro + packages/integrations/node/test/fixtures/trailing-slash: dependencies: '@astrojs/node': @@ -5614,7 +5623,7 @@ importers: dependencies: '@preact/preset-vite': specifier: ^2.10.1 - version: 2.10.1(@babel/core@7.27.4)(preact@10.26.9)(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1)) + version: 2.10.1(@babel/core@7.27.7)(preact@10.26.9)(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1)) '@preact/signals': specifier: ^2.2.0 version: 2.2.0(preact@10.26.9) @@ -5774,10 +5783,10 @@ importers: dependencies: '@sveltejs/vite-plugin-svelte': specifier: ^5.1.0 - version: 5.1.0(svelte@5.34.7)(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1)) + version: 5.1.0(svelte@5.34.9)(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1)) svelte2tsx: specifier: ^0.7.40 - version: 0.7.40(svelte@5.34.7)(typescript@5.8.3) + version: 0.7.40(svelte@5.34.9)(typescript@5.8.3) vite: specifier: ^6.3.5 version: 6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1) @@ -5790,19 +5799,16 @@ importers: version: link:../../../scripts svelte: specifier: ^5.34.7 - version: 5.34.7 + version: 5.34.9 packages/integrations/vercel: dependencies: '@astrojs/internal-helpers': specifier: workspace:* version: link:../../internal-helpers - '@astrojs/underscore-redirects': - specifier: workspace:* - version: link:../../underscore-redirects '@vercel/analytics': specifier: ^1.5.0 - version: 1.5.0(react@19.1.0)(svelte@5.34.7)(vue@3.5.17(typescript@5.8.3)) + version: 1.5.0(react@19.1.0)(svelte@5.34.9)(vue@3.5.17(typescript@5.8.3)) '@vercel/edge': specifier: ^1.2.1 version: 1.2.1 @@ -6451,8 +6457,8 @@ packages: resolution: {integrity: sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==} engines: {node: '>=6.9.0'} - '@babel/core@7.27.4': - resolution: {integrity: sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==} + '@babel/core@7.27.7': + resolution: {integrity: sha512-BU2f9tlKQ5CAthiMIgpzAh4eDTLWo1mqi9jqE2OxMG0E/OM199VJt2q8BztTxpnSW0i1ymdwLXRJnYzvDM5r2w==} engines: {node: '>=6.9.0'} '@babel/generator@7.27.5': @@ -6525,8 +6531,8 @@ packages: resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==} engines: {node: '>=6.9.0'} - '@babel/parser@7.27.5': - resolution: {integrity: sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==} + '@babel/parser@7.27.7': + resolution: {integrity: sha512-qnzXzDXdr/po3bOTbTIQZ7+TxNKxpkN5IifVLXS+r7qwynkZfPyjZfE7hCXbo7IoO9TNcSyibgONsf2HauUd3Q==} engines: {node: '>=6.0.0'} hasBin: true @@ -6603,16 +6609,16 @@ packages: resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.27.4': - resolution: {integrity: sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==} + '@babel/traverse@7.27.7': + resolution: {integrity: sha512-X6ZlfR/O/s5EQ/SnUSLzr+6kGnkg8HXGMzpgsMsrJVcfDtH1vIp6ctCN4eZ1LS5c0+te5Cb6Y514fASjMRJ1nw==} engines: {node: '>=6.9.0'} '@babel/types@7.26.10': resolution: {integrity: sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==} engines: {node: '>=6.9.0'} - '@babel/types@7.27.6': - resolution: {integrity: sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==} + '@babel/types@7.27.7': + resolution: {integrity: sha512-8OLQgDScAOHXnAz2cV+RfzzNMipuLVBz2biuAJFMV9bfkNf393je3VM8CLkjQodW5+iWsSJdSgSWT6rsZoXHPw==} engines: {node: '>=6.9.0'} '@biomejs/biome@2.0.0': @@ -6787,8 +6793,8 @@ packages: cpu: [x64] os: [win32] - '@cloudflare/workers-types@4.20250607.0': - resolution: {integrity: sha512-OYmKNzC2eQy6CNj+j0go8Ut3SezjsprCgJyEaBzJql+473WAN9ndVnNZy9lj/tTyLV6wzpQkZWmRAKGDmacvkg==} + '@cloudflare/workers-types@4.20250507.0': + resolution: {integrity: sha512-fkrq7A6XWgPEmXJDwu9TS/Zw7qho3eGlrOlicKue32Bdul0Ma4Mym4sh7ZeGbayBXxkOx/fHpm5GfhVEtngRWQ==} '@codspeed/core@4.0.1': resolution: {integrity: sha512-fJ53arfgtzCDZa8DuGJhpTZ3Ll9A1uW5nQ2jSJnfO4Hl5MRD2cP8P4vPvIUAGbdbjwCxR1jat6cW8OloMJkJXw==} @@ -12592,8 +12598,8 @@ packages: svelte: ^3.55 || ^4.0.0-next.0 || ^4.0 || ^5.0.0-next.0 typescript: ^4.9.4 || ^5.0.0 - svelte@5.34.7: - resolution: {integrity: sha512-5PEg+QQKce4t1qiOtVUhUS3AQRTtxJyGBTpxLcNWnr0Ve8q4r06bMo0Gv8uhtCPWlztZHoi3Ye7elLhu+PCTMg==} + svelte@5.34.9: + resolution: {integrity: sha512-sld35zFpooaSRSj4qw8Vl/cyyK0/sLQq9qhJ7BGZo/Kd0ggYtEnvNYLlzhhoqYsYQzA0hJqkzt3RBO/8KoTZOg==} engines: {node: '>=18'} svgo@3.3.2: @@ -12830,8 +12836,8 @@ packages: resolution: {integrity: sha512-BUgJXc752Kou3oOIuU1i+yZZypyZRqNPW0vqoMPl8VaoalSfeR0D8/t4iAS3yirs79SSMTxTag+ZC86uswv+Cw==} engines: {node: '>=18.17'} - undici@7.10.0: - resolution: {integrity: sha512-u5otvFBOBZvmdjWLVW+5DAc9Nkq8f24g0O9oY7qw2JVIF1VocIFoyz9JFkuVOS2j41AufeO0xnlweJ2RLT8nGw==} + undici@7.11.0: + resolution: {integrity: sha512-heTSIac3iLhsmZhUCjyS3JQEkZELateufzZuBaVM5RHXdSBMb1LPMQf5x+FH7qjsZYDP0ttAc3nnVpUB+wYbOg==} engines: {node: '>=20.18.1'} unenv@2.0.0-rc.15: @@ -13622,18 +13628,18 @@ snapshots: '@babel/compat-data@7.27.2': {} - '@babel/core@7.27.4': + '@babel/core@7.27.7': dependencies: '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.27.1 '@babel/generator': 7.27.5 '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.4) + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.7) '@babel/helpers': 7.27.6 - '@babel/parser': 7.27.5 + '@babel/parser': 7.27.7 '@babel/template': 7.27.2 - '@babel/traverse': 7.27.4 - '@babel/types': 7.27.6 + '@babel/traverse': 7.27.7 + '@babel/types': 7.27.7 convert-source-map: 2.0.0 debug: 4.4.1 gensync: 1.0.0-beta.2 @@ -13644,15 +13650,15 @@ snapshots: '@babel/generator@7.27.5': dependencies: - '@babel/parser': 7.27.5 - '@babel/types': 7.27.6 + '@babel/parser': 7.27.7 + '@babel/types': 7.27.7 '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 jsesc: 3.0.2 '@babel/helper-annotate-as-pure@7.27.1': dependencies: - '@babel/types': 7.27.6 + '@babel/types': 7.27.7 '@babel/helper-compilation-targets@7.27.2': dependencies: @@ -13662,65 +13668,65 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.27.1(@babel/core@7.27.4)': + '@babel/helper-create-class-features-plugin@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-annotate-as-pure': 7.27.1 '@babel/helper-member-expression-to-functions': 7.27.1 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.27.4) + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.27.7) '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.27.4 + '@babel/traverse': 7.27.7 semver: 6.3.1 transitivePeerDependencies: - supports-color '@babel/helper-member-expression-to-functions@7.27.1': dependencies: - '@babel/traverse': 7.27.4 - '@babel/types': 7.27.6 + '@babel/traverse': 7.27.7 + '@babel/types': 7.27.7 transitivePeerDependencies: - supports-color '@babel/helper-module-imports@7.18.6': dependencies: - '@babel/types': 7.27.6 + '@babel/types': 7.27.7 '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.27.4 - '@babel/types': 7.27.6 + '@babel/traverse': 7.27.7 + '@babel/types': 7.27.7 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.27.3(@babel/core@7.27.4)': + '@babel/helper-module-transforms@7.27.3(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-module-imports': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.27.4 + '@babel/traverse': 7.27.7 transitivePeerDependencies: - supports-color '@babel/helper-optimise-call-expression@7.27.1': dependencies: - '@babel/types': 7.27.6 + '@babel/types': 7.27.7 '@babel/helper-plugin-utils@7.27.1': {} - '@babel/helper-replace-supers@7.27.1(@babel/core@7.27.4)': + '@babel/helper-replace-supers@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-member-expression-to-functions': 7.27.1 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.27.4 + '@babel/traverse': 7.27.7 transitivePeerDependencies: - supports-color '@babel/helper-skip-transparent-expression-wrappers@7.27.1': dependencies: - '@babel/traverse': 7.27.4 - '@babel/types': 7.27.6 + '@babel/traverse': 7.27.7 + '@babel/types': 7.27.7 transitivePeerDependencies: - supports-color @@ -13733,82 +13739,82 @@ snapshots: '@babel/helpers@7.27.6': dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.27.6 + '@babel/types': 7.27.7 - '@babel/parser@7.27.5': + '@babel/parser@7.27.7': dependencies: - '@babel/types': 7.27.6 + '@babel/types': 7.27.7 - '@babel/plugin-proposal-decorators@7.24.7(@babel/core@7.27.4)': + '@babel/plugin-proposal-decorators@7.24.7(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.4) + '@babel/core': 7.27.7 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.7) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-decorators': 7.24.7(@babel/core@7.27.4) + '@babel/plugin-syntax-decorators': 7.24.7(@babel/core@7.27.7) transitivePeerDependencies: - supports-color - '@babel/plugin-syntax-decorators@7.24.7(@babel/core@7.27.4)': + '@babel/plugin-syntax-decorators@7.24.7(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-attributes@7.25.6(@babel/core@7.27.4)': + '@babel/plugin-syntax-import-attributes@7.25.6(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.27.4)': + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.27.4)': + '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-react-jsx-development@7.25.9(@babel/core@7.27.4)': + '@babel/plugin-transform-react-jsx-development@7.25.9(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 - '@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.27.4) + '@babel/core': 7.27.7 + '@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.27.7) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.27.4)': + '@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-annotate-as-pure': 7.27.1 '@babel/helper-module-imports': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.27.4) - '@babel/types': 7.27.6 + '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.27.7) + '@babel/types': 7.27.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-typescript@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-typescript@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-annotate-as-pure': 7.27.1 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.4) + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.7) '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.27.7) transitivePeerDependencies: - supports-color @@ -13819,16 +13825,16 @@ snapshots: '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 - '@babel/parser': 7.27.5 - '@babel/types': 7.27.6 + '@babel/parser': 7.27.7 + '@babel/types': 7.27.7 - '@babel/traverse@7.27.4': + '@babel/traverse@7.27.7': dependencies: '@babel/code-frame': 7.27.1 '@babel/generator': 7.27.5 - '@babel/parser': 7.27.5 + '@babel/parser': 7.27.7 '@babel/template': 7.27.2 - '@babel/types': 7.27.6 + '@babel/types': 7.27.7 debug: 4.4.1 globals: 11.12.0 transitivePeerDependencies: @@ -13839,7 +13845,7 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/types@7.27.6': + '@babel/types@7.27.7': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 @@ -14084,7 +14090,7 @@ snapshots: '@cloudflare/workerd-windows-64@1.20250428.0': optional: true - '@cloudflare/workers-types@4.20250607.0': {} + '@cloudflare/workers-types@4.20250507.0': {} '@codspeed/core@4.0.1': dependencies: @@ -15080,7 +15086,7 @@ snapshots: '@netlify/zip-it-and-ship-it@10.0.7(rollup@4.40.2)': dependencies: - '@babel/parser': 7.27.5 + '@babel/parser': 7.27.7 '@babel/types': 7.26.10 '@netlify/binary-info': 1.0.0 '@netlify/serverless-functions-api': 1.38.0 @@ -15199,14 +15205,14 @@ snapshots: '@polka/url@1.0.0-next.25': {} - '@preact/preset-vite@2.10.1(@babel/core@7.27.4)(preact@10.26.9)(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1))': + '@preact/preset-vite@2.10.1(@babel/core@7.27.7)(preact@10.26.9)(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1))': dependencies: - '@babel/core': 7.27.4 - '@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.27.4) - '@babel/plugin-transform-react-jsx-development': 7.25.9(@babel/core@7.27.4) + '@babel/core': 7.27.7 + '@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.27.7) + '@babel/plugin-transform-react-jsx-development': 7.25.9(@babel/core@7.27.7) '@prefresh/vite': 2.4.5(preact@10.26.9)(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1)) '@rollup/pluginutils': 4.2.1 - babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.27.4) + babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.27.7) debug: 4.4.1 kolorist: 1.8.0 vite: 6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1) @@ -15237,7 +15243,7 @@ snapshots: '@prefresh/vite@2.4.5(preact@10.26.9)(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1))': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@prefresh/babel-plugin': 0.5.1 '@prefresh/core': 1.5.2(preact@10.26.9) '@prefresh/utils': 1.2.0 @@ -15373,23 +15379,23 @@ snapshots: dependencies: acorn: 8.14.1 - '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.1.0(svelte@5.34.7)(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1)))(svelte@5.34.7)(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1))': + '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.1.0(svelte@5.34.9)(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1)))(svelte@5.34.9)(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1))': dependencies: - '@sveltejs/vite-plugin-svelte': 5.1.0(svelte@5.34.7)(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1)) + '@sveltejs/vite-plugin-svelte': 5.1.0(svelte@5.34.9)(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1)) debug: 4.4.1 - svelte: 5.34.7 + svelte: 5.34.9 vite: 6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@5.1.0(svelte@5.34.7)(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1))': + '@sveltejs/vite-plugin-svelte@5.1.0(svelte@5.34.9)(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.1.0(svelte@5.34.7)(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1)))(svelte@5.34.7)(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1)) + '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.1.0(svelte@5.34.9)(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1)))(svelte@5.34.9)(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1)) debug: 4.4.1 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.17 - svelte: 5.34.7 + svelte: 5.34.9 vite: 6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1) vitefu: 1.0.6(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1)) transitivePeerDependencies: @@ -15472,24 +15478,24 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.27.5 - '@babel/types': 7.27.6 + '@babel/parser': 7.27.7 + '@babel/types': 7.27.7 '@types/babel__generator': 7.6.8 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.20.6 '@types/babel__generator@7.6.8': dependencies: - '@babel/types': 7.27.6 + '@babel/types': 7.27.7 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.27.5 - '@babel/types': 7.27.6 + '@babel/parser': 7.27.7 + '@babel/types': 7.27.7 '@types/babel__traverse@7.20.6': dependencies: - '@babel/types': 7.27.6 + '@babel/types': 7.27.7 '@types/canvas-confetti@1.9.0': {} @@ -15761,10 +15767,10 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@vercel/analytics@1.5.0(react@19.1.0)(svelte@5.34.7)(vue@3.5.17(typescript@5.8.3))': + '@vercel/analytics@1.5.0(react@19.1.0)(svelte@5.34.9)(vue@3.5.17(typescript@5.8.3))': optionalDependencies: react: 19.1.0 - svelte: 5.34.7 + svelte: 5.34.9 vue: 3.5.17(typescript@5.8.3) '@vercel/edge@1.2.1': {} @@ -15816,9 +15822,9 @@ snapshots: '@vitejs/plugin-react@4.6.0(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1))': dependencies: - '@babel/core': 7.27.4 - '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.27.4) + '@babel/core': 7.27.7 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.27.7) '@rolldown/pluginutils': 1.0.0-beta.19 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 @@ -15828,10 +15834,10 @@ snapshots: '@vitejs/plugin-vue-jsx@4.2.0(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1))(vue@3.5.17(typescript@5.8.3))': dependencies: - '@babel/core': 7.27.4 - '@babel/plugin-transform-typescript': 7.27.1(@babel/core@7.27.4) + '@babel/core': 7.27.7 + '@babel/plugin-transform-typescript': 7.27.1(@babel/core@7.27.7) '@rolldown/pluginutils': 1.0.0-beta.19 - '@vue/babel-plugin-jsx': 1.4.0(@babel/core@7.27.4) + '@vue/babel-plugin-jsx': 1.4.0(@babel/core@7.27.7) vite: 6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1) vue: 3.5.17(typescript@5.8.3) transitivePeerDependencies: @@ -15934,36 +15940,36 @@ snapshots: '@vue/babel-helper-vue-transform-on@1.4.0': {} - '@vue/babel-plugin-jsx@1.4.0(@babel/core@7.27.4)': + '@vue/babel-plugin-jsx@1.4.0(@babel/core@7.27.7)': dependencies: '@babel/helper-module-imports': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.27.4) + '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.27.7) '@babel/template': 7.27.2 - '@babel/traverse': 7.27.4 - '@babel/types': 7.27.6 + '@babel/traverse': 7.27.7 + '@babel/types': 7.27.7 '@vue/babel-helper-vue-transform-on': 1.4.0 - '@vue/babel-plugin-resolve-type': 1.4.0(@babel/core@7.27.4) + '@vue/babel-plugin-resolve-type': 1.4.0(@babel/core@7.27.7) '@vue/shared': 3.5.17 optionalDependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 transitivePeerDependencies: - supports-color - '@vue/babel-plugin-resolve-type@1.4.0(@babel/core@7.27.4)': + '@vue/babel-plugin-resolve-type@1.4.0(@babel/core@7.27.7)': dependencies: '@babel/code-frame': 7.27.1 - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-module-imports': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/parser': 7.27.5 + '@babel/parser': 7.27.7 '@vue/compiler-sfc': 3.5.17 transitivePeerDependencies: - supports-color '@vue/compiler-core@3.5.17': dependencies: - '@babel/parser': 7.27.5 + '@babel/parser': 7.27.7 '@vue/shared': 3.5.17 entities: 4.5.0 estree-walker: 2.0.2 @@ -15976,7 +15982,7 @@ snapshots: '@vue/compiler-sfc@3.5.17': dependencies: - '@babel/parser': 7.27.5 + '@babel/parser': 7.27.7 '@vue/compiler-core': 3.5.17 '@vue/compiler-dom': 3.5.17 '@vue/compiler-ssr': 3.5.17 @@ -16300,23 +16306,23 @@ snapshots: axobject-query@4.1.0: {} - babel-plugin-jsx-dom-expressions@0.38.5(@babel/core@7.27.4): + babel-plugin-jsx-dom-expressions@0.38.5(@babel/core@7.27.7): dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-module-imports': 7.18.6 - '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.27.4) - '@babel/types': 7.27.6 + '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.27.7) + '@babel/types': 7.27.7 html-entities: 2.3.3 validate-html-nesting: 1.2.2 - babel-plugin-transform-hook-names@1.0.2(@babel/core@7.27.4): + babel-plugin-transform-hook-names@1.0.2(@babel/core@7.27.7): dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 - babel-preset-solid@1.8.22(@babel/core@7.27.4): + babel-preset-solid@1.8.22(@babel/core@7.27.7): dependencies: - '@babel/core': 7.27.4 - babel-plugin-jsx-dom-expressions: 0.38.5(@babel/core@7.27.4) + '@babel/core': 7.27.7 + babel-plugin-jsx-dom-expressions: 0.38.5(@babel/core@7.27.7) bail@2.0.2: {} @@ -16508,7 +16514,7 @@ snapshots: parse5: 7.3.0 parse5-htmlparser2-tree-adapter: 7.1.0 parse5-parser-stream: 7.1.2 - undici: 7.10.0 + undici: 7.11.0 whatwg-mimetype: 4.0.0 chokidar@4.0.3: @@ -16908,9 +16914,9 @@ snapshots: dotenv@8.6.0: {} - drizzle-orm@0.42.0(@cloudflare/workers-types@4.20250607.0)(@libsql/client@0.15.2): + drizzle-orm@0.42.0(@cloudflare/workers-types@4.20250507.0)(@libsql/client@0.15.2): optionalDependencies: - '@cloudflare/workers-types': 4.20250607.0 + '@cloudflare/workers-types': 4.20250507.0 '@libsql/client': 0.15.2 dset@3.1.4: {} @@ -18308,8 +18314,8 @@ snapshots: magicast@0.3.5: dependencies: - '@babel/parser': 7.27.5 - '@babel/types': 7.27.6 + '@babel/parser': 7.27.7 + '@babel/types': 7.27.7 source-map-js: 1.2.1 make-dir@3.1.0: @@ -19012,7 +19018,7 @@ snapshots: node-source-walk@6.0.2: dependencies: - '@babel/parser': 7.27.5 + '@babel/parser': 7.27.7 nopt@5.0.0: dependencies: @@ -20301,7 +20307,7 @@ snapshots: dependencies: '@babel/generator': 7.27.5 '@babel/helper-module-imports': 7.27.1 - '@babel/types': 7.27.6 + '@babel/types': 7.27.7 solid-js: 1.9.7 transitivePeerDependencies: - supports-color @@ -20460,14 +20466,14 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte2tsx@0.7.40(svelte@5.34.7)(typescript@5.8.3): + svelte2tsx@0.7.40(svelte@5.34.9)(typescript@5.8.3): dependencies: dedent-js: 1.0.1 pascal-case: 3.1.2 - svelte: 5.34.7 + svelte: 5.34.9 typescript: 5.8.3 - svelte@5.34.7: + svelte@5.34.9: dependencies: '@ampproject/remapping': 2.3.0 '@jridgewell/sourcemap-codec': 1.5.0 @@ -20683,7 +20689,7 @@ snapshots: undici@6.21.0: {} - undici@7.10.0: {} + undici@7.11.0: {} unenv@2.0.0-rc.15: dependencies: @@ -20912,9 +20918,9 @@ snapshots: vite-plugin-solid@2.11.7(solid-js@1.9.7)(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1)): dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@types/babel__core': 7.20.5 - babel-preset-solid: 1.8.22(@babel/core@7.27.4) + babel-preset-solid: 1.8.22(@babel/core@7.27.7) merge-anything: 5.1.7 solid-js: 1.9.7 solid-refresh: 0.6.3(solid-js@1.9.7) @@ -20941,12 +20947,12 @@ snapshots: vite-plugin-vue-inspector@5.3.1(vite@6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1)): dependencies: - '@babel/core': 7.27.4 - '@babel/plugin-proposal-decorators': 7.24.7(@babel/core@7.27.4) - '@babel/plugin-syntax-import-attributes': 7.25.6(@babel/core@7.27.4) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.27.4) - '@babel/plugin-transform-typescript': 7.27.1(@babel/core@7.27.4) - '@vue/babel-plugin-jsx': 1.4.0(@babel/core@7.27.4) + '@babel/core': 7.27.7 + '@babel/plugin-proposal-decorators': 7.24.7(@babel/core@7.27.7) + '@babel/plugin-syntax-import-attributes': 7.25.6(@babel/core@7.27.7) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.27.7) + '@babel/plugin-transform-typescript': 7.27.1(@babel/core@7.27.7) + '@vue/babel-plugin-jsx': 1.4.0(@babel/core@7.27.7) '@vue/compiler-dom': 3.5.17 kolorist: 1.8.0 magic-string: 0.30.17 @@ -21229,7 +21235,7 @@ snapshots: '@cloudflare/workerd-linux-arm64': 1.20250428.0 '@cloudflare/workerd-windows-64': 1.20250428.0 - wrangler@4.14.1(@cloudflare/workers-types@4.20250607.0): + wrangler@4.14.1(@cloudflare/workers-types@4.20250507.0): dependencies: '@cloudflare/kv-asset-handler': 0.4.0 '@cloudflare/unenv-preset': 2.3.1(unenv@2.0.0-rc.15)(workerd@1.20250428.0) @@ -21240,7 +21246,7 @@ snapshots: unenv: 2.0.0-rc.15 workerd: 1.20250428.0 optionalDependencies: - '@cloudflare/workers-types': 4.20250607.0 + '@cloudflare/workers-types': 4.20250507.0 fsevents: 2.3.3 sharp: 0.33.5 transitivePeerDependencies: