diff --git a/.changeset/lemon-bobcats-kick.md b/.changeset/lemon-bobcats-kick.md new file mode 100644 index 000000000000..444863635afd --- /dev/null +++ b/.changeset/lemon-bobcats-kick.md @@ -0,0 +1,5 @@ +--- +'astro': major +--- + +Default preview host to `localhost` instead of `127.0.0.1`. This allows the static server and integration preview servers to serve under ipv6. diff --git a/packages/astro/package.json b/packages/astro/package.json index 7191f8488051..43f25171d500 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -155,8 +155,8 @@ "rehype": "^12.0.1", "resolve": "^1.22.0", "semver": "^7.3.7", + "server-destroy": "^1.0.1", "shiki": "^0.11.1", - "sirv": "^2.0.2", "slash": "^4.0.0", "string-width": "^5.1.2", "strip-ansi": "^7.0.1", @@ -171,7 +171,7 @@ "zod": "^3.17.3" }, "devDependencies": { - "@playwright/test": "^1.22.2", + "@playwright/test": "^1.29.2", "@types/babel__generator": "^7.6.4", "@types/babel__traverse": "^7.17.1", "@types/chai": "^4.3.1", @@ -191,6 +191,7 @@ "@types/resolve": "^1.20.2", "@types/rimraf": "^3.0.2", "@types/send": "^0.17.1", + "@types/server-destroy": "^1.0.1", "@types/unist": "^2.0.6", "astro-scripts": "workspace:*", "chai": "^4.3.6", diff --git a/packages/astro/src/core/dev/dev.ts b/packages/astro/src/core/dev/dev.ts index 62c01ce78579..074501b04a34 100644 --- a/packages/astro/src/core/dev/dev.ts +++ b/packages/astro/src/core/dev/dev.ts @@ -49,9 +49,6 @@ export default async function dev( // Start listening to the port const devServerAddressInfo = await startContainer(restart.container); - const site = settings.config.site - ? new URL(settings.config.base, settings.config.site) - : undefined; info( options.logging, null, @@ -59,7 +56,7 @@ export default async function dev( startupTime: performance.now() - devStart, resolvedUrls: restart.container.viteServer.resolvedUrls || { local: [], network: [] }, host: settings.config.server.host, - site, + base: settings.config.base, isRestart: options.isRestart, }) ); diff --git a/packages/astro/src/core/messages.ts b/packages/astro/src/core/messages.ts index d46bf99916e3..817958876d7b 100644 --- a/packages/astro/src/core/messages.ts +++ b/packages/astro/src/core/messages.ts @@ -14,14 +14,11 @@ import { underline, yellow, } from 'kleur/colors'; -import type { AddressInfo } from 'net'; -import os from 'os'; import { ResolvedServerUrls } from 'vite'; import { ZodError } from 'zod'; import { renderErrorMarkdown } from './errors/dev/utils.js'; import { AstroError, CompilerError, ErrorWithMetadata } from './errors/index.js'; -import { removeTrailingForwardSlash } from './path.js'; -import { emoji, getLocalAddress, padMultilineString } from './util.js'; +import { emoji, padMultilineString } from './util.js'; const PREFIX_PADDING = 6; @@ -58,31 +55,26 @@ export function serverStart({ startupTime, resolvedUrls, host, - site, + base, isRestart = false, }: { startupTime: number; resolvedUrls: ResolvedServerUrls; host: string | boolean; - site: URL | undefined; + base: string; isRestart?: boolean; }): string { // PACKAGE_VERSION is injected at build-time const version = process.env.PACKAGE_VERSION ?? '0.0.0'; - const rootPath = site ? site.pathname : '/'; const localPrefix = `${dim('┃')} Local `; const networkPrefix = `${dim('┃')} Network `; const emptyPrefix = ' '.repeat(11); const localUrlMessages = resolvedUrls.local.map((url, i) => { - return `${i === 0 ? localPrefix : emptyPrefix}${bold( - cyan(removeTrailingForwardSlash(url) + rootPath) - )}`; + return `${i === 0 ? localPrefix : emptyPrefix}${bold(cyan(new URL(url).origin + base))}`; }); const networkUrlMessages = resolvedUrls.network.map((url, i) => { - return `${i === 0 ? networkPrefix : emptyPrefix}${bold( - cyan(removeTrailingForwardSlash(url) + rootPath) - )}`; + return `${i === 0 ? networkPrefix : emptyPrefix}${bold(cyan(new URL(url).origin + base))}`; }); if (networkUrlMessages.length === 0) { @@ -109,50 +101,6 @@ export function serverStart({ .join('\n'); } -export function resolveServerUrls({ - address, - host, - https, -}: { - address: AddressInfo; - host: string | boolean; - https: boolean; -}): ResolvedServerUrls { - const { address: networkAddress, port } = address; - const localAddress = getLocalAddress(networkAddress, host); - const networkLogging = getNetworkLogging(host); - const toDisplayUrl = (hostname: string) => `${https ? 'https' : 'http'}://${hostname}:${port}`; - - let local = toDisplayUrl(localAddress); - let network: string | null = null; - - if (networkLogging === 'visible') { - const ipv4Networks = Object.values(os.networkInterfaces()) - .flatMap((networkInterface) => networkInterface ?? []) - .filter( - (networkInterface) => - networkInterface?.address && - // Node < v18 - ((typeof networkInterface.family === 'string' && networkInterface.family === 'IPv4') || - // Node >= v18 - (typeof networkInterface.family === 'number' && (networkInterface as any).family === 4)) - ); - for (let { address: ipv4Address } of ipv4Networks) { - if (ipv4Address.includes('127.0.0.1')) { - const displayAddress = ipv4Address.replace('127.0.0.1', localAddress); - local = toDisplayUrl(displayAddress); - } else { - network = toDisplayUrl(ipv4Address); - } - } - } - - return { - local: [local], - network: network ? [network] : [], - }; -} - export function telemetryNotice() { const headline = yellow(`Astro now collects ${bold('anonymous')} usage data.`); const why = `This ${bold('optional program')} will help shape our roadmap.`; @@ -228,11 +176,6 @@ export function cancelled(message: string, tip?: string) { .join('\n'); } -/** Display port in use */ -export function portInUse({ port }: { port: number }): string { - return `Port ${port} in use. Trying a new one…`; -} - const LOCAL_IP_HOSTS = new Set(['localhost', '127.0.0.1']); export function getNetworkLogging(host: string | boolean): 'none' | 'host-to-expose' | 'visible' { diff --git a/packages/astro/src/core/preview/index.ts b/packages/astro/src/core/preview/index.ts index d4a5c8bb2cb5..4488e590387a 100644 --- a/packages/astro/src/core/preview/index.ts +++ b/packages/astro/src/core/preview/index.ts @@ -23,11 +23,9 @@ export default async function preview( logging: logging, }); await runHookConfigDone({ settings: settings, logging: logging }); - const host = getResolvedHostForHttpServer(settings.config.server.host); - const { port, headers } = settings.config.server; if (settings.config.output === 'static') { - const server = await createStaticPreviewServer(settings, { logging, host, port, headers }); + const server = await createStaticPreviewServer(settings, logging); return server; } if (!settings.adapter) { @@ -55,8 +53,8 @@ export default async function preview( outDir: settings.config.outDir, client: settings.config.build.client, serverEntrypoint: new URL(settings.config.build.serverEntry, settings.config.build.server), - host, - port, + host: getResolvedHostForHttpServer(settings.config.server.host), + port: settings.config.server.port, base: settings.config.base, }); diff --git a/packages/astro/src/core/preview/static-preview-server.ts b/packages/astro/src/core/preview/static-preview-server.ts index f74755aed550..8f5a88103920 100644 --- a/packages/astro/src/core/preview/static-preview-server.ts +++ b/packages/astro/src/core/preview/static-preview-server.ts @@ -1,15 +1,14 @@ -import type { AddressInfo } from 'net'; -import type { AstroSettings } from '../../@types/astro'; -import type { LogOptions } from '../logger/core'; - -import fs from 'fs'; -import http, { OutgoingHttpHeaders } from 'http'; +import http from 'http'; import { performance } from 'perf_hooks'; -import sirv from 'sirv'; import { fileURLToPath } from 'url'; -import { notFoundTemplate, subpathNotUsedTemplate } from '../../template/4xx.js'; +import { preview, type PreviewServer as VitePreviewServer } from 'vite'; +import type { AstroSettings } from '../../@types/astro'; +import type { LogOptions } from '../logger/core'; import { error, info } from '../logger/core.js'; import * as msg from '../messages.js'; +import { getResolvedHostForHttpServer } from './util.js'; +import { vitePluginAstroPreview } from './vite-plugin-astro-preview.js'; +import enableDestroy from 'server-destroy'; export interface PreviewServer { host?: string; @@ -19,160 +18,65 @@ export interface PreviewServer { stop(): Promise; } -const HAS_FILE_EXTENSION_REGEXP = /^.*\.[^\\]+$/; - -/** The primary dev action */ export default async function createStaticPreviewServer( settings: AstroSettings, - { - logging, - host, - port, - headers, - }: { - logging: LogOptions; - host: string | undefined; - port: number; - headers: OutgoingHttpHeaders | undefined; - } + logging: LogOptions ): Promise { const startServerTime = performance.now(); - const defaultOrigin = 'http://localhost'; - const trailingSlash = settings.config.trailingSlash; - /** Base request URL. */ - let baseURL = new URL(settings.config.base, new URL(settings.config.site || '/', defaultOrigin)); - const staticFileServer = sirv(fileURLToPath(settings.config.outDir), { - dev: true, - etag: true, - maxAge: 0, - setHeaders: (res, pathname, stats) => { - for (const [name, value] of Object.entries(headers ?? {})) { - if (value) res.setHeader(name, value); - } - }, - }); - // Create the preview server, send static files out of the `dist/` directory. - const server = http.createServer((req, res) => { - const requestURL = new URL(req.url as string, defaultOrigin); - - // respond 404 to requests outside the base request directory - if (!requestURL.pathname.startsWith(baseURL.pathname)) { - res.statusCode = 404; - res.end(subpathNotUsedTemplate(baseURL.pathname, requestURL.pathname)); - return; - } - - /** Relative request path. */ - const pathname = requestURL.pathname.slice(baseURL.pathname.length - 1); - const isRoot = pathname === '/'; - const hasTrailingSlash = isRoot || pathname.endsWith('/'); - - function sendError(message: string) { - res.statusCode = 404; - res.end(notFoundTemplate(pathname, message)); - } - - switch (true) { - case hasTrailingSlash && trailingSlash == 'never' && !isRoot: - sendError('Not Found (trailingSlash is set to "never")'); - return; - case !hasTrailingSlash && - trailingSlash == 'always' && - !isRoot && - !HAS_FILE_EXTENSION_REGEXP.test(pathname): - sendError('Not Found (trailingSlash is set to "always")'); - return; - default: { - // HACK: rewrite req.url so that sirv finds the file - req.url = '/' + req.url?.replace(baseURL.pathname, ''); - staticFileServer(req, res, () => { - const errorPagePath = fileURLToPath(settings.config.outDir + '/404.html'); - if (fs.existsSync(errorPagePath)) { - res.statusCode = 404; - res.setHeader('Content-Type', 'text/html;charset=utf-8'); - res.end(fs.readFileSync(errorPagePath)); - } else { - staticFileServer(req, res, () => { - sendError('Not Found'); - }); - } - }); - return; - } - } - }); - - let httpServer: http.Server; - - /** Expose dev server to `port` */ - function startServer(timerStart: number): Promise { - let showedPortTakenMsg = false; - let showedListenMsg = false; - return new Promise((resolve, reject) => { - const listen = () => { - httpServer = server.listen(port, host, async () => { - if (!showedListenMsg) { - const resolvedUrls = msg.resolveServerUrls({ - address: server.address() as AddressInfo, - host: settings.config.server.host, - https: false, - }); - info( - logging, - null, - msg.serverStart({ - startupTime: performance.now() - timerStart, - resolvedUrls, - host: settings.config.server.host, - site: baseURL, - }) - ); - } - showedListenMsg = true; - resolve(); - }); - httpServer?.on('error', onError); - }; - - const onError = (err: NodeJS.ErrnoException) => { - if (err.code && err.code === 'EADDRINUSE') { - if (!showedPortTakenMsg) { - info(logging, 'astro', msg.portInUse({ port })); - showedPortTakenMsg = true; // only print this once - } - port++; - return listen(); // retry - } else { - error(logging, 'astro', err.stack || err.message); - httpServer?.removeListener('error', onError); - reject(err); // reject - } - }; - - listen(); + let previewServer: VitePreviewServer; + try { + previewServer = await preview({ + configFile: false, + base: settings.config.base, + appType: 'mpa', + build: { + outDir: fileURLToPath(settings.config.outDir), + }, + preview: { + host: settings.config.server.host, + port: settings.config.server.port, + headers: settings.config.server.headers, + }, + plugins: [vitePluginAstroPreview(settings)], }); + } catch (err) { + if (err instanceof Error) { + error(logging, 'astro', err.stack || err.message); + } + throw err; } - // Start listening on `hostname:port`. - await startServer(startServerTime); + enableDestroy(previewServer.httpServer); + + // Log server start URLs + info( + logging, + null, + msg.serverStart({ + startupTime: performance.now() - startServerTime, + resolvedUrls: previewServer.resolvedUrls, + host: settings.config.server.host, + base: settings.config.base, + }) + ); // Resolves once the server is closed function closed() { return new Promise((resolve, reject) => { - httpServer!.addListener('close', resolve); - httpServer!.addListener('error', reject); + previewServer.httpServer.addListener('close', resolve); + previewServer.httpServer.addListener('error', reject); }); } return { - host, - port, + host: getResolvedHostForHttpServer(settings.config.server.host), + port: settings.config.server.port, closed, - server: httpServer!, + server: previewServer.httpServer, stop: async () => { await new Promise((resolve, reject) => { - httpServer.close((err) => (err ? reject(err) : resolve(undefined))); + previewServer.httpServer.destroy((err) => (err ? reject(err) : resolve(undefined))); }); }, }; diff --git a/packages/astro/src/core/preview/util.ts b/packages/astro/src/core/preview/util.ts index 556946d2bd32..d02e4c3c8a09 100644 --- a/packages/astro/src/core/preview/util.ts +++ b/packages/astro/src/core/preview/util.ts @@ -1,7 +1,7 @@ export function getResolvedHostForHttpServer(host: string | boolean) { if (host === false) { // Use a secure default - return '127.0.0.1'; + return 'localhost'; } else if (host === true) { // If passed --host in the CLI without arguments return undefined; // undefined typically means 0.0.0.0 or :: (listen on all IPs) @@ -9,3 +9,11 @@ export function getResolvedHostForHttpServer(host: string | boolean) { return host; } } + +export function stripBase(path: string, base: string): string { + if (path === base) { + return '/'; + } + const baseWithSlash = base.endsWith('/') ? base : base + '/'; + return path.replace(RegExp('^' + baseWithSlash), '/'); +} diff --git a/packages/astro/src/core/preview/vite-plugin-astro-preview.ts b/packages/astro/src/core/preview/vite-plugin-astro-preview.ts new file mode 100644 index 000000000000..95ff4d225996 --- /dev/null +++ b/packages/astro/src/core/preview/vite-plugin-astro-preview.ts @@ -0,0 +1,68 @@ +import fs from 'fs'; +import { fileURLToPath } from 'url'; +import { Plugin } from 'vite'; +import { AstroSettings } from '../../@types/astro.js'; +import { notFoundTemplate, subpathNotUsedTemplate } from '../../template/4xx.js'; +import { stripBase } from './util.js'; + +const HAS_FILE_EXTENSION_REGEXP = /^.*\.[^\\]+$/; + +export function vitePluginAstroPreview(settings: AstroSettings): Plugin { + const { base, outDir, trailingSlash } = settings.config; + + return { + name: 'astro:preview', + apply: 'serve', + configurePreviewServer(server) { + server.middlewares.use((req, res, next) => { + // respond 404 to requests outside the base request directory + if (!req.url!.startsWith(base)) { + res.statusCode = 404; + res.end(subpathNotUsedTemplate(base, req.url!)); + return; + } + + const pathname = stripBase(req.url!, base); + const isRoot = pathname === '/'; + + // Validate trailingSlash + if (!isRoot) { + const hasTrailingSlash = pathname.endsWith('/'); + + if (hasTrailingSlash && trailingSlash == 'never') { + res.statusCode = 404; + res.end(notFoundTemplate(pathname, 'Not Found (trailingSlash is set to "never")')); + return; + } + + if ( + !hasTrailingSlash && + trailingSlash == 'always' && + !HAS_FILE_EXTENSION_REGEXP.test(pathname) + ) { + res.statusCode = 404; + res.end(notFoundTemplate(pathname, 'Not Found (trailingSlash is set to "always")')); + return; + } + } + + next(); + }); + + return () => { + server.middlewares.use((req, res) => { + const errorPagePath = fileURLToPath(outDir + '/404.html'); + if (fs.existsSync(errorPagePath)) { + res.statusCode = 404; + res.setHeader('Content-Type', 'text/html;charset=utf-8'); + res.end(fs.readFileSync(errorPagePath)); + } else { + const pathname = stripBase(req.url!, base); + res.statusCode = 404; + res.end(notFoundTemplate(pathname, 'Not Found')); + } + }); + }; + }, + }; +} diff --git a/packages/astro/src/core/util.ts b/packages/astro/src/core/util.ts index d6f95062a958..79148797b5ae 100644 --- a/packages/astro/src/core/util.ts +++ b/packages/astro/src/core/util.ts @@ -164,14 +164,6 @@ export function emoji(char: string, fallback: string) { return process.platform !== 'win32' ? char : fallback; } -export function getLocalAddress(serverAddress: string, host: string | boolean): string { - if (typeof host === 'boolean' || host === 'localhost') { - return 'localhost'; - } else { - return serverAddress; - } -} - /** * Simulate Vite's resolve and import analysis so we can import the id as an URL * through a script tag or a dynamic import as-is. diff --git a/packages/astro/test/cli.test.js b/packages/astro/test/cli.test.js index fb5d54d38fa8..2c1dd0dead9d 100644 --- a/packages/astro/test/cli.test.js +++ b/packages/astro/test/cli.test.js @@ -77,17 +77,11 @@ describe('astro cli', () => { const localURL = new URL(local); const networkURL = new URL(network); - if (cmd === 'dev') { - expect(localURL.hostname).to.be.oneOf( - ['localhost', '127.0.0.1'], - `Expected local URL to be on localhost` - ); - } else { - expect(localURL.hostname).to.be.equal( - flagValue ?? 'localhost', - `Expected local URL to be on localhost` - ); - } + expect(localURL.hostname).to.be.oneOf( + ['localhost', '127.0.0.1'], + `Expected local URL to be on localhost` + ); + // Note: our tests run in parallel so this could be 3000+! expect(Number.parseInt(localURL.port)).to.be.greaterThanOrEqual( 3000, @@ -113,17 +107,11 @@ describe('astro cli', () => { expect(network).to.not.be.undefined; const localURL = new URL(local); - if (cmd === 'dev') { - expect(localURL.hostname).to.be.oneOf( - ['localhost', '127.0.0.1'], - `Expected local URL to be on localhost` - ); - } else { - expect(localURL.hostname).to.be.equal( - 'localhost', - `Expected local URL to be on localhost` - ); - } + expect(localURL.hostname).to.be.oneOf( + ['localhost', '127.0.0.1'], + `Expected local URL to be on localhost` + ); + expect(() => new URL(networkURL)).to.throw(); }); }); @@ -140,14 +128,10 @@ describe('astro cli', () => { expect(network).to.be.undefined; const localURL = new URL(local); - if (cmd === 'dev') { - expect(localURL.hostname).to.be.oneOf( - ['localhost', '127.0.0.1'], - `Expected local URL to be on localhost` - ); - } else { - expect(localURL.hostname).to.be.equal(flagValue, `Expected local URL to be on localhost`); - } + expect(localURL.hostname).to.be.oneOf( + ['localhost', '127.0.0.1'], + `Expected local URL to be on localhost` + ); }); }); }); diff --git a/packages/integrations/prefetch/package.json b/packages/integrations/prefetch/package.json index 11f9bdc8ea00..414bd2c33697 100644 --- a/packages/integrations/prefetch/package.json +++ b/packages/integrations/prefetch/package.json @@ -31,13 +31,13 @@ "test:match": "playwright test -g" }, "devDependencies": { - "@playwright/test": "^1.26.0", + "@playwright/test": "^1.29.2", "@types/chai": "^4.3.1", "@types/chai-as-promised": "^7.1.5", "@types/mocha": "^9.1.1", "astro": "workspace:*", "astro-scripts": "workspace:*", - "playwright": "^1.22.2" + "playwright": "^1.29.2" }, "dependencies": { "throttles": "^1.0.1" diff --git a/packages/integrations/prefetch/test/basic-prefetch.test.js b/packages/integrations/prefetch/test/basic-prefetch.test.js index 576bd19bd2e3..6bab8a478d08 100644 --- a/packages/integrations/prefetch/test/basic-prefetch.test.js +++ b/packages/integrations/prefetch/test/basic-prefetch.test.js @@ -19,7 +19,7 @@ test.describe('Basic prefetch', () => { test('skips /admin', async ({ page, astro }) => { const requests = []; - page.on('request', async (request) => requests.push(request.url())); + page.on('request', (request) => requests.push(request.url())); await page.goto(astro.resolveUrl('/')); @@ -56,7 +56,7 @@ test.describe('Basic prefetch', () => { test('skips /admin', async ({ page, astro }) => { const requests = []; - page.on('request', async (request) => requests.push(request.url())); + page.on('request', (request) => requests.push(request.url())); await page.goto(astro.resolveUrl('/')); diff --git a/packages/integrations/prefetch/test/custom-selectors.test.js b/packages/integrations/prefetch/test/custom-selectors.test.js index d57ac3b908cb..803e1dc3b401 100644 --- a/packages/integrations/prefetch/test/custom-selectors.test.js +++ b/packages/integrations/prefetch/test/custom-selectors.test.js @@ -27,7 +27,7 @@ test.describe('Custom prefetch selectors', () => { test('only prefetches /contact', async ({ page, astro }) => { const requests = []; - page.on('request', async (request) => requests.push(request.url())); + page.on('request', (request) => requests.push(request.url())); await page.goto(astro.resolveUrl('/')); @@ -64,7 +64,7 @@ test.describe('Custom prefetch selectors', () => { test('only prefetches /contact', async ({ page, astro }) => { const requests = []; - page.on('request', async (request) => requests.push(request.url())); + page.on('request', (request) => requests.push(request.url())); await page.goto(astro.resolveUrl('/')); diff --git a/packages/integrations/prefetch/test/style-prefetch.test.js b/packages/integrations/prefetch/test/style-prefetch.test.js index 8e89a35e336a..b9bb0b04369a 100644 --- a/packages/integrations/prefetch/test/style-prefetch.test.js +++ b/packages/integrations/prefetch/test/style-prefetch.test.js @@ -39,7 +39,7 @@ test.describe('Style prefetch', () => { test('style fetching', async ({ page, astro }) => { const requests = []; - page.on('request', async (request) => requests.push(request.url())); + page.on('request', (request) => requests.push(request.url())); await page.goto(astro.resolveUrl('/')); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 85466367163f..6434adaf6f79 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -396,7 +396,7 @@ importers: '@babel/plugin-transform-react-jsx': ^7.17.12 '@babel/traverse': ^7.18.2 '@babel/types': ^7.18.4 - '@playwright/test': ^1.22.2 + '@playwright/test': ^1.29.2 '@types/babel__core': ^7.1.19 '@types/babel__generator': ^7.6.4 '@types/babel__traverse': ^7.17.1 @@ -418,6 +418,7 @@ importers: '@types/resolve': ^1.20.2 '@types/rimraf': ^3.0.2 '@types/send': ^0.17.1 + '@types/server-destroy': ^1.0.1 '@types/unist': ^2.0.6 '@types/yargs-parser': ^21.0.0 acorn: ^8.8.1 @@ -466,8 +467,8 @@ importers: rollup: ^3.9.0 sass: ^1.52.2 semver: ^7.3.7 + server-destroy: ^1.0.1 shiki: ^0.11.1 - sirv: ^2.0.2 slash: ^4.0.0 srcset-parse: ^1.1.0 string-width: ^5.1.2 @@ -529,8 +530,8 @@ importers: rehype: 12.0.1 resolve: 1.22.1 semver: 7.3.8 + server-destroy: 1.0.1 shiki: 0.11.1 - sirv: 2.0.2 slash: 4.0.0 string-width: 5.1.2 strip-ansi: 7.0.1 @@ -564,6 +565,7 @@ importers: '@types/resolve': 1.20.2 '@types/rimraf': 3.0.2 '@types/send': 0.17.1 + '@types/server-destroy': 1.0.1 '@types/unist': 2.0.6 astro-scripts: link:../../scripts chai: 4.3.7 @@ -3139,13 +3141,13 @@ importers: packages/integrations/prefetch: specifiers: - '@playwright/test': ^1.26.0 + '@playwright/test': ^1.29.2 '@types/chai': ^4.3.1 '@types/chai-as-promised': ^7.1.5 '@types/mocha': ^9.1.1 astro: workspace:* astro-scripts: workspace:* - playwright: ^1.22.2 + playwright: ^1.29.2 throttles: ^1.0.1 dependencies: throttles: 1.0.1 @@ -7055,6 +7057,7 @@ packages: /@types/node/14.18.36: resolution: {integrity: sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ==} + dev: true /@types/node/16.18.11: resolution: {integrity: sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==} @@ -7126,7 +7129,7 @@ packages: /@types/resolve/1.17.1: resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} dependencies: - '@types/node': 14.18.36 + '@types/node': 18.11.18 /@types/resolve/1.20.2: resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} @@ -7141,7 +7144,7 @@ packages: /@types/sax/1.2.4: resolution: {integrity: sha512-pSAff4IAxJjfAXUG6tFkO7dsSbTmf8CtUpfhhZ5VhkRpC4628tJhh3+V6H1E+/Gs9piSzYKT5yzHO5M4GG9jkw==} dependencies: - '@types/node': 17.0.45 + '@types/node': 18.11.18 dev: false /@types/scheduler/0.16.2: @@ -7162,6 +7165,12 @@ packages: '@types/node': 18.11.18 dev: true + /@types/server-destroy/1.0.1: + resolution: {integrity: sha512-77QGr7waZbE0Y0uF+G+uH3H3SmhyA78Jf2r5r7QSrpg0U3kSXduWpGjzP9PvPLR/KCy+kHjjpnugRHsYTnHopg==} + dependencies: + '@types/node': 18.11.18 + dev: true + /@types/set-cookie-parser/2.4.2: resolution: {integrity: sha512-fBZgytwhYAUkj/jC/FAV4RQ5EerRup1YQsXQCh8rZfiHkc4UahC192oH0smGwsXol3cL3A5oETuAHeQHmhXM4w==} dependencies: @@ -11129,7 +11138,7 @@ packages: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 14.18.36 + '@types/node': 18.11.18 merge-stream: 2.0.0 supports-color: 7.2.0 @@ -13994,6 +14003,10 @@ packages: randombytes: 2.1.0 dev: true + /server-destroy/1.0.1: + resolution: {integrity: sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==} + dev: false + /set-blocking/2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}