Drop Node 14 support (#5782)

* chore: Update engines field

* fix(deps): Remove node-fetch

* feat(polyfills): Remove node-fetch for undici

* feat(webapi): Remove node-fetch from the webapis polyfills for undici

* feat(core): Remove node-fetch for undici in Astro core

* feat(telemetry): Remove node-fetch for undici

* feat(node): Remove node-fetch for undici in node integration

* feat(vercel): Remove node-fetch for undici in Vercel integration

* chore: update lockfile

* chore: update lockfile

* chore: changeset

* fix(set): Fix set directives not streaming correctly on Node 16

* Try another approach

* Debugging

* Debug fetch

* Use global fetch if there is one

* changeset for lit

* Remove web-streams-polyfill

* Remove web-streams-polyfill license note

* Update .changeset/stupid-wolves-explain.md

Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com>

Co-authored-by: Matthew Phillips <matthew@skypack.dev>
Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com>
This commit is contained in:
Erika 2023-01-09 22:59:20 +01:00 committed by GitHub
parent 9bb08bfe8c
commit 1f92d64ea3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 147 additions and 387 deletions

View file

@ -0,0 +1,5 @@
---
'@astrojs/lit': patch
---
Only shim fetch if not already present

View file

@ -0,0 +1,16 @@
---
'astro': major
'@astrojs/prism': major
'create-astro': major
'@astrojs/mdx': minor
'@astrojs/node': major
'@astrojs/preact': major
'@astrojs/react': major
'@astrojs/solid-js': major
'@astrojs/svelte': major
'@astrojs/vercel': major
'@astrojs/vue': major
'@astrojs/telemetry': major
---
Remove support for Node 14. Minimum supported Node version is now >=16.12.0

View file

@ -0,0 +1,7 @@
---
'@astrojs/webapi': major
---
Replace node-fetch's polyfill with undici.
Since `undici` does not support it, this change also removes custom support for the `file:` protocol

View file

@ -35,6 +35,6 @@
"@types/prismjs": "1.26.0"
},
"engines": {
"node": "^14.18.0 || >=16.12.0"
"node": ">=16.12.0"
}
}

View file

@ -50,7 +50,7 @@ async function main() {
// it's okay to hard-code the valid Node versions here since they will not change over time.
if (typeof require === 'undefined') {
console.error(`\nNode.js v${version} is not supported by Astro!
Please upgrade to a version of Node.js with complete ESM support: "^14.18.0 || >=16.12.0"\n`);
Please upgrade to a supported version of Node.js: ">=16.12.0"\n`);
}
// Not supported: Report the most helpful error message possible.

View file

@ -199,7 +199,6 @@
"eol": "^0.9.1",
"memfs": "^3.4.7",
"mocha": "^9.2.2",
"node-fetch": "^3.2.5",
"node-mocks-http": "^1.11.0",
"rehype-autolink-headings": "^6.1.1",
"rehype-slug": "^5.0.1",
@ -208,10 +207,11 @@
"rollup": "^3.9.0",
"sass": "^1.52.2",
"srcset-parse": "^1.1.0",
"undici": "^5.14.0",
"unified": "^10.1.2"
},
"engines": {
"node": "^14.18.0 || >=16.12.0",
"node": ">=16.12.0",
"npm": ">=6.14.0"
}
}

View file

@ -1,4 +1,5 @@
import { escape } from 'html-escaper';
import { streamAsyncIterator } from './util.js';
// Leverage the battle-tested `html-escaper` npm package.
export const escapeHTML = escape;
@ -58,9 +59,19 @@ export function isHTMLBytes(value: any): value is HTMLBytes {
return Object.prototype.toString.call(value) === '[object HTMLBytes]';
}
async function* unescapeChunksAsync(iterable: AsyncIterable<Uint8Array>): any {
for await (const chunk of iterable) {
yield unescapeHTML(chunk as BlessedType);
function hasGetReader(obj: unknown): obj is ReadableStream {
return typeof (obj as any).getReader === 'function';
}
async function* unescapeChunksAsync(iterable: ReadableStream | string): any {
if (hasGetReader(iterable)) {
for await (const chunk of streamAsyncIterator(iterable)) {
yield unescapeHTML(chunk as BlessedType);
}
} else {
for await (const chunk of iterable) {
yield unescapeHTML(chunk as BlessedType);
}
}
}
@ -82,7 +93,7 @@ export function unescapeHTML(
}
// If a response, stream out the chunks
else if (str instanceof Response && str.body) {
const body = str.body as unknown as AsyncIterable<Uint8Array>;
const body = str.body;
return unescapeChunksAsync(body);
}
// If a promise, await the result and mark that.
@ -92,7 +103,7 @@ export function unescapeHTML(
});
} else if (Symbol.iterator in str) {
return unescapeChunks(str);
} else if (Symbol.asyncIterator in str) {
} else if (Symbol.asyncIterator in str || hasGetReader(str)) {
return unescapeChunksAsync(str);
}
}

View file

@ -1,3 +1,5 @@
import { streamAsyncIterator } from './util.js';
const isNodeJS =
typeof process === 'object' && Object.prototype.toString.call(process) === '[object process]';
@ -21,9 +23,9 @@ function createResponseClass() {
async text(): Promise<string> {
if (this.#isStream && isNodeJS) {
let decoder = new TextDecoder();
let body = this.#body as AsyncIterable<Uint8Array>;
let body = this.#body;
let out = '';
for await (let chunk of body) {
for await (let chunk of streamAsyncIterator(body)) {
out += decoder.decode(chunk);
}
return out;
@ -33,10 +35,10 @@ function createResponseClass() {
async arrayBuffer(): Promise<ArrayBuffer> {
if (this.#isStream && isNodeJS) {
let body = this.#body as AsyncIterable<Uint8Array>;
let body = this.#body;
let chunks: Uint8Array[] = [];
let len = 0;
for await (let chunk of body) {
for await (let chunk of streamAsyncIterator(body)) {
chunks.push(chunk);
len += chunk.length;
}

View file

@ -31,3 +31,17 @@ export function serializeListValue(value: any) {
export function isPromise<T = any>(value: any): value is Promise<T> {
return !!value && typeof value === 'object' && typeof value.then === 'function';
}
export async function* streamAsyncIterator(stream: ReadableStream) {
const reader = stream.getReader();
try {
while (true) {
const { done, value } = await reader.read();
if (done) return;
yield value;
}
} finally {
reader.releaseLock();
}
}

View file

@ -1,7 +1,7 @@
import { expect } from 'chai';
import { loadFixture } from './test-utils.js';
import { File, FormData } from 'undici';
import testAdapter from './test-adapter.js';
import { FormData, File } from 'node-fetch';
import { loadFixture } from './test-utils.js';
describe('API routes in SSR', () => {
/** @type {import('./test-utils').Fixture} */

View file

@ -1,7 +1,7 @@
import { isWindows, loadFixture } from './test-utils.js';
import { expect } from 'chai';
import testAdapter from './test-adapter.js';
import * as cheerio from 'cheerio';
import testAdapter from './test-adapter.js';
import { isWindows, loadFixture, streamAsyncIterator } from './test-utils.js';
describe('Streaming', () => {
if (isWindows) return;
@ -32,7 +32,7 @@ describe('Streaming', () => {
it('Body is chunked', async () => {
let res = await fixture.fetch('/');
let chunks = [];
for await (const bytes of res.body) {
for await (const bytes of streamAsyncIterator(res.body)) {
let chunk = bytes.toString('utf-8');
chunks.push(chunk);
}
@ -61,7 +61,7 @@ describe('Streaming', () => {
const response = await app.render(request);
let chunks = [];
let decoder = new TextDecoder();
for await (const bytes of response.body) {
for await (const bytes of streamAsyncIterator(response.body)) {
let chunk = decoder.decode(bytes);
chunks.push(chunk);
}
@ -102,7 +102,7 @@ describe('Streaming disabled', () => {
it('Body is chunked', async () => {
let res = await fixture.fetch('/');
let chunks = [];
for await (const bytes of res.body) {
for await (const bytes of streamAsyncIterator(res.body)) {
let chunk = bytes.toString('utf-8');
chunks.push(chunk);
}

View file

@ -19,7 +19,7 @@ polyfill(globalThis, {
});
/**
* @typedef {import('node-fetch').Response} Response
* @typedef {import('undici').Response} Response
* @typedef {import('../src/core/dev/dev').DedvServer} DevServer
* @typedef {import('../src/@types/astro').AstroConfig} AstroConfig
* @typedef {import('../src/core/preview/index').PreviewServer} PreviewServer
@ -303,3 +303,17 @@ export const isWindows = os.platform() === 'win32';
export function fixLineEndings(str) {
return str.replace(/\r\n/g, '\n');
}
export async function* streamAsyncIterator(stream) {
const reader = stream.getReader();
try {
while (true) {
const { done, value } = await reader.read();
if (done) return;
yield value;
}
} finally {
reader.releaseLock();
}
}

View file

@ -54,6 +54,6 @@
"uvu": "^0.5.3"
},
"engines": {
"node": "^14.18.0 || >=16.12.0"
"node": ">=16.12.0"
}
}

View file

@ -1,5 +1,12 @@
import { installWindowOnGlobal } from '@lit-labs/ssr/lib/dom-shim.js';
installWindowOnGlobal();
if(typeof fetch === 'function') {
const _fetch = fetch;
installWindowOnGlobal();
globalThis.fetch = window.fetch = _fetch;
} else {
installWindowOnGlobal();
}
window.global = window;
document.getElementsByTagName = () => [];

View file

@ -71,6 +71,6 @@
"vite": "^4.0.3"
},
"engines": {
"node": "^14.18.0 || >=16.12.0"
"node": ">=16.12.0"
}
}

View file

@ -37,12 +37,12 @@
"astro": "workspace:^2.0.0-beta.0"
},
"devDependencies": {
"@types/node-fetch": "^2.6.2",
"@types/send": "^0.17.1",
"astro": "workspace:*",
"astro-scripts": "workspace:*",
"chai": "^4.3.6",
"mocha": "^9.2.2",
"node-mocks-http": "^1.11.0"
"node-mocks-http": "^1.11.0",
"undici": "^5.14.0"
}
}

View file

@ -4,7 +4,7 @@
* - https://github.com/apollographql/apollo-client/blob/main/src/utilities/common/responseIterator.ts
*/
import type { Response as NodeResponse } from 'node-fetch';
import type { Response as NodeResponse } from 'undici';
import { Readable as NodeReadableStream } from 'stream';
interface NodeStreamIterator<T> {

View file

@ -47,6 +47,6 @@
"preact": "^10.6.5"
},
"engines": {
"node": "^14.18.0 || >=16.12.0"
"node": ">=16.12.0"
}
}

View file

@ -52,6 +52,6 @@
"@types/react-dom": "^17.0.17 || ^18.0.6"
},
"engines": {
"node": "^14.18.0 || >=16.12.0"
"node": ">=16.12.0"
}
}

View file

@ -44,6 +44,6 @@
"solid-js": "^1.4.3"
},
"engines": {
"node": "^14.18.0 || >=16.12.0"
"node": ">=16.12.0"
}
}

View file

@ -47,6 +47,6 @@
"astro": "workspace:^2.0.0-beta.0"
},
"engines": {
"node": "^14.18.0 || >=16.12.0"
"node": ">=16.12.0"
}
}

View file

@ -3,21 +3,12 @@ import type { SSRManifest } from 'astro';
import { App } from 'astro/app';
import type { IncomingMessage, ServerResponse } from 'node:http';
import * as requestTransformLegacy from './request-transform/legacy.js';
import * as requestTransformNode18 from './request-transform/node18.js';
import { getRequest, setResponse } from './request-transform';
polyfill(globalThis, {
exclude: 'window document',
});
// Node 18+ has a new API for request/response, while older versions use node-fetch
// When we drop support for Node 14, we can remove the legacy code by switching to undici
const nodeVersion = parseInt(process.version.split('.')[0].slice(1)); // 'v14.17.0' -> 14
const { getRequest, setResponse } =
nodeVersion >= 18 ? requestTransformNode18 : requestTransformLegacy;
export const createExports = (manifest: SSRManifest) => {
const app = new App(manifest);

View file

@ -1,111 +0,0 @@
import type { App } from 'astro/app';
import type { IncomingMessage, ServerResponse } from 'node:http';
import { Readable } from 'node:stream';
const clientAddressSymbol = Symbol.for('astro.clientAddress');
/*
Credits to the SvelteKit team
https://github.com/sveltejs/kit/blob/69913e9fda054fa6a62a80e2bb4ee7dca1005796/packages/kit/src/node.js
*/
function get_raw_body(req: IncomingMessage) {
return new Promise<Uint8Array | null>((fulfil, reject) => {
const h = req.headers;
if (!h['content-type']) {
return fulfil(null);
}
req.on('error', reject);
const length = Number(h['content-length']);
// https://github.com/jshttp/type-is/blob/c1f4388c71c8a01f79934e68f630ca4a15fffcd6/index.js#L81-L95
if (isNaN(length) && h['transfer-encoding'] == null) {
return fulfil(null);
}
let data = new Uint8Array(length || 0);
if (length > 0) {
let offset = 0;
req.on('data', (chunk) => {
const new_len = offset + Buffer.byteLength(chunk);
if (new_len > length) {
return reject({
status: 413,
reason: 'Exceeded "Content-Length" limit',
});
}
data.set(chunk, offset);
offset = new_len;
});
} else {
req.on('data', (chunk) => {
const new_data = new Uint8Array(data.length + chunk.length);
new_data.set(data, 0);
new_data.set(chunk, data.length);
data = new_data;
});
}
req.on('end', () => {
fulfil(data);
});
});
}
export async function getRequest(base: string, req: IncomingMessage): Promise<Request> {
let headers = req.headers as Record<string, string>;
if (req.httpVersionMajor === 2) {
// we need to strip out the HTTP/2 pseudo-headers because node-fetch's
// Request implementation doesn't like them
headers = Object.assign({}, headers);
delete headers[':method'];
delete headers[':path'];
delete headers[':authority'];
delete headers[':scheme'];
}
const request = new Request(base + req.url, {
method: req.method,
headers,
body: await get_raw_body(req), // TODO stream rather than buffer
});
Reflect.set(request, clientAddressSymbol, headers['x-forwarded-for']);
return request;
}
export async function setResponse(
app: App,
res: ServerResponse,
response: Response
): Promise<void> {
const headers = Object.fromEntries(response.headers);
if (response.headers.has('set-cookie')) {
// @ts-expect-error (headers.raw() is non-standard)
headers['set-cookie'] = response.headers.raw()['set-cookie'];
}
if (app.setCookieHeaders) {
const setCookieHeaders: Array<string> = Array.from(app.setCookieHeaders(response));
if (setCookieHeaders.length) {
res.setHeader('Set-Cookie', setCookieHeaders);
}
}
res.writeHead(response.status, headers);
if (response.body instanceof Readable) {
response.body.pipe(res);
} else {
if (response.body) {
res.write(await response.arrayBuffer());
}
res.end();
}
}

View file

@ -54,6 +54,6 @@
"astro": "workspace:^2.0.0-beta.0"
},
"engines": {
"node": "^14.18.0 || >=16.12.0"
"node": ">=16.12.0"
}
}

View file

@ -32,7 +32,7 @@
"dset": "^3.1.2",
"is-docker": "^3.0.0",
"is-wsl": "^2.2.0",
"node-fetch": "^3.2.5",
"undici": "^5.14.0",
"which-pm-runs": "^1.1.0"
},
"devDependencies": {
@ -45,6 +45,6 @@
"mocha": "^9.2.2"
},
"engines": {
"node": "^14.18.0 || >=16.12.0"
"node": ">=16.12.0"
}
}

View file

@ -1,4 +1,4 @@
import fetch from 'node-fetch';
import { fetch } from 'undici';
const ASTRO_TELEMETRY_ENDPOINT = `https://telemetry.astro.build/api/v1/record`;

View file

@ -31,7 +31,3 @@ Code from [event-target-shim](https://www.npmjs.com/package/event-target-shim) i
Code from [fetch-blob](https://www.npmjs.com/package/fetch-blob) is licensed under the MIT License (MIT), Copyright Jimmy Wärting.
Code from [formdata-polyfill](https://www.npmjs.com/package/formdata-polyfill) is licensed under the MIT License (MIT), Copyright Jimmy Wärting.
Code from [node-fetch](https://www.npmjs.com/package/node-fetch) is licensed under the MIT License (MIT), Copyright Node Fetch Team.
Code from [web-streams-polyfill](https://www.npmjs.com/package/web-streams-polyfill) is licensed under the MIT License (MIT), Copyright Mattias Buelens and Diwank Singh Tomer.

View file

@ -173,7 +173,3 @@ Code from [event-target-shim](https://www.npmjs.com/package/event-target-shim) i
Code from [fetch-blob](https://www.npmjs.com/package/fetch-blob) is licensed under the MIT License (MIT), Copyright Jimmy Wärting.
Code from [formdata-polyfill](https://www.npmjs.com/package/formdata-polyfill) is licensed under the MIT License (MIT), Copyright Jimmy Wärting.
Code from [node-fetch](https://www.npmjs.com/package/node-fetch) is licensed under the MIT License (MIT), Copyright Node Fetch Team.
Code from [web-streams-polyfill](https://www.npmjs.com/package/web-streams-polyfill) is licensed under the MIT License (MIT), Copyright Mattias Buelens and Diwank Singh Tomer.

View file

@ -51,7 +51,7 @@
"homepage": "https://github.com/withastro/astro/tree/main/packages/webapi#readme",
"dependencies": {
"global-agent": "^3.0.0",
"node-fetch": "^3.2.5"
"undici": "^5.14.0"
},
"devDependencies": {
"@rollup/plugin-alias": "^3.1.9",
@ -74,8 +74,7 @@
"rollup-plugin-terser": "^7.0.2",
"tslib": "^2.4.0",
"typescript": "~4.7.3",
"urlpattern-polyfill": "^1.0.0-rc5",
"web-streams-polyfill": "^3.2.1"
"urlpattern-polyfill": "^1.0.0-rc5"
},
"scripts": {
"build": "node run/build.js",

View file

@ -1,17 +1,17 @@
import { rollup } from 'rollup'
import { default as alias } from '@rollup/plugin-alias'
import { default as inject } from '@rollup/plugin-inject'
import { nodeResolve } from '@rollup/plugin-node-resolve'
import path from 'node:path'
import { createRequire } from 'node:module'
import { default as typescript } from '@rollup/plugin-typescript'
import { default as MagicString } from 'magic-string'
import {
readFile as nodeReadFile,
rename,
rm,
writeFile,
} from 'node:fs/promises'
import { default as MagicString } from 'magic-string'
import { default as alias } from '@rollup/plugin-alias'
import { default as inject } from '@rollup/plugin-inject'
import { default as typescript } from '@rollup/plugin-typescript'
import { createRequire } from 'node:module'
import path from 'node:path'
import { rollup } from 'rollup'
const readFileCache = Object.create(null)
const require = createRequire(import.meta.url)
@ -76,13 +76,13 @@ const plugins = [
MediaQueryList: ['./MediaQueryList', 'MediaQueryList'],
Node: ['./Node', 'Node'],
ReadableStream: [
'web-streams-polyfill/dist/ponyfill.es6.mjs',
'node:stream/web',
'ReadableStream',
],
ShadowRoot: ['./Node', 'ShadowRoot'],
Window: ['./Window', 'Window'],
'globalThis.ReadableStream': [
'web-streams-polyfill/dist/ponyfill.es6.mjs',
'node:stream/web',
'ReadableStream',
],
}),
@ -178,7 +178,7 @@ async function build() {
inputOptions: {
input: 'src/polyfill.ts',
plugins: plugins,
external: ['node-fetch', 'global-agent'],
external: ['undici', 'global-agent'],
onwarn(warning, warn) {
if (warning.code !== 'UNRESOLVED_IMPORT') warn(warning)
},

View file

@ -1,102 +0,0 @@
import { bootstrap as bootstrapGlobalAgent } from 'global-agent'
import type { RequestInit } from 'node-fetch'
import { default as nodeFetch, Headers, Request, Response } from 'node-fetch'
import Stream from 'node:stream'
import * as _ from './utils'
bootstrapGlobalAgent({
environmentVariableNamespace: '',
})
export { Headers, Request, Response }
export const fetch = {
fetch(
resource: string | Request,
init?: Partial<FetchInit>
): Promise<Response> {
const resourceURL = new URL(
_.__object_isPrototypeOf(Request.prototype, resource)
? (resource as Request).url
: _.pathToPosix(resource),
typeof Object(globalThis.process).cwd === 'function'
? 'file:' + _.pathToPosix(process.cwd()) + '/'
: 'file:'
)
if (resourceURL.protocol.toLowerCase() === 'file:') {
return import('node:fs').then((fs) => {
try {
const stats = fs.statSync(resourceURL)
const body = fs.createReadStream(resourceURL)
return new Response(body, {
status: 200,
statusText: '',
headers: {
'content-length': String(stats.size),
date: new Date().toUTCString(),
'last-modified': new Date(stats.mtimeMs).toUTCString(),
},
})
} catch (error) {
const body = new Stream.Readable()
body._read = () => {}
body.push(null)
return new Response(body, {
status: 404,
statusText: '',
headers: {
date: new Date().toUTCString(),
},
})
}
})
} else {
return nodeFetch(resource, init)
}
},
}.fetch
type USVString = {} & string
interface FetchInit {
body: RequestInit['body']
cache:
| 'default'
| 'no-store'
| 'reload'
| 'no-cache'
| 'force-cache'
| 'only-if-cached'
credentials: 'omit' | 'same-origin' | 'include'
headers: Headers | Record<string, string>
method:
| 'GET'
| 'HEAD'
| 'POST'
| 'PUT'
| 'DELETE'
| 'CONNECT'
| 'OPTIONS'
| 'TRACE'
| 'PATCH'
| USVString
mode: 'cors' | 'no-cors' | 'same-origin' | USVString
redirect: 'follow' | 'manual' | 'error'
referrer: USVString
referrerPolicy:
| 'no-referrer'
| 'no-referrer-when-downgrade'
| 'same-origin'
| 'origin'
| 'strict-origin'
| 'origin-when-cross-origin'
| 'strict-origin-when-cross-origin'
| 'unsafe-url'
integrity: USVString
keepalive: boolean
signal: AbortSignal
}

View file

@ -7,6 +7,7 @@ import {
import { Event, EventTarget } from 'event-target-shim'
import { Blob, File } from 'fetch-blob/from.js'
import { FormData } from 'formdata-polyfill/esm.min.js'
import * as undici from 'undici'
import { URLPattern } from 'urlpattern-polyfill'
import {
ByteLengthQueuingStrategy,
@ -21,7 +22,7 @@ import {
WritableStream,
WritableStreamDefaultController,
WritableStreamDefaultWriter,
} from 'web-streams-polyfill/dist/ponyfill.es6.mjs'
} from 'node:stream/web'
import {
cancelAnimationFrame,
requestAnimationFrame,
@ -30,7 +31,6 @@ import { atob, btoa } from './lib/Base64'
import { CharacterData, Comment, Text } from './lib/CharacterData'
import { CustomEvent } from './lib/CustomEvent'
import { DOMException } from './lib/DOMException'
import { fetch, Headers, Request, Response } from './lib/fetch'
import { cancelIdleCallback, requestIdleCallback } from './lib/IdleCallback'
import structuredClone from './lib/structuredClone'
import { clearTimeout, setTimeout } from './lib/Timeout'
@ -83,6 +83,11 @@ import { initPromise } from './lib/Promise'
import { initRelativeIndexingMethod } from './lib/RelativeIndexingMethod'
import { initString } from './lib/String'
const fetch = undici.fetch
const Headers = undici.Headers
const Response = undici.Response
const Request = undici.Request
export {
AbortController,
AbortSignal,

View file

@ -3,5 +3,3 @@ declare module '@ungap/structured-clone/esm/index.js'
declare module '@ungap/structured-clone/esm/deserialize.js'
declare module '@ungap/structured-clone/esm/serialize.js'
declare module 'abort-controller/dist/abort-controller.mjs'
declare module 'node-fetch/src/index.js'
declare module 'web-streams-polyfill/dist/ponyfill.es6.mjs'

View file

@ -22,66 +22,6 @@ describe('Fetch', () => {
expect(json).to.be.an('array')
})
it('Fetch with file', async () => {
const { fetch } = target
const url = new URL('../package.json', import.meta.url)
const response = await fetch(url)
expect(response.constructor).to.equal(target.Response)
expect(response.status).to.equal(200)
expect(response.statusText).to.be.empty
expect(response.headers.has('date')).to.equal(true)
expect(response.headers.has('content-length')).to.equal(true)
expect(response.headers.has('last-modified')).to.equal(true)
const json = await response.json()
expect(json.name).to.equal('@astrojs/webapi')
})
it('Fetch with missing file', async () => {
const { fetch } = target
const url = new URL('../missing.json', import.meta.url)
const response = await fetch(url)
expect(response.constructor).to.equal(target.Response)
expect(response.status).to.equal(404)
expect(response.statusText).to.be.empty
expect(response.headers.has('date')).to.equal(true)
expect(response.headers.has('content-length')).to.equal(false)
expect(response.headers.has('last-modified')).to.equal(false)
})
it('Fetch with (file) Request', async () => {
const { Request, fetch } = target
const request = new Request(new URL('../package.json', import.meta.url))
const response = await fetch(request)
expect(response.constructor).to.equal(target.Response)
const json = await response.json()
expect(json.name).to.equal('@astrojs/webapi')
})
it('Fetch with relative file', async () => {
const { fetch } = target
const response = await fetch('package.json')
const json = await response.json()
expect(json.name).to.equal('@astrojs/webapi')
})
it('Fetch with data', async () => {
const { fetch } = target

View file

@ -448,7 +448,6 @@ importers:
memfs: ^3.4.7
mime: ^3.0.0
mocha: ^9.2.2
node-fetch: ^3.2.5
node-mocks-http: ^1.11.0
ora: ^6.1.0
path-browserify: ^1.0.1
@ -476,6 +475,7 @@ importers:
supports-esm: ^1.0.0
tsconfig-resolver: ^3.0.1
typescript: '*'
undici: ^5.14.0
unified: ^10.1.2
unist-util-visit: ^4.1.0
vfile: ^5.3.2
@ -573,7 +573,6 @@ importers:
eol: 0.9.1
memfs: 3.4.13
mocha: 9.2.2
node-fetch: 3.3.0
node-mocks-http: 1.12.1
rehype-autolink-headings: 6.1.1
rehype-slug: 5.1.0
@ -582,6 +581,7 @@ importers:
rollup: 3.9.1
sass: 1.57.1
srcset-parse: 1.1.0
undici: 5.14.0
unified: 10.1.2
packages/astro-prism:
@ -3077,7 +3077,6 @@ importers:
packages/integrations/node:
specifiers:
'@astrojs/webapi': ^1.1.1
'@types/node-fetch': ^2.6.2
'@types/send': ^0.17.1
astro: workspace:*
astro-scripts: workspace:*
@ -3085,17 +3084,18 @@ importers:
mocha: ^9.2.2
node-mocks-http: ^1.11.0
send: ^0.18.0
undici: ^5.14.0
dependencies:
'@astrojs/webapi': link:../../webapi
send: 0.18.0
devDependencies:
'@types/node-fetch': 2.6.2
'@types/send': 0.17.1
astro: link:../../astro
astro-scripts: link:../../../scripts
chai: 4.3.7
mocha: 9.2.2
node-mocks-http: 1.12.1
undici: 5.14.0
packages/integrations/node/test/fixtures/api-route:
specifiers:
@ -3536,7 +3536,7 @@ importers:
is-docker: ^3.0.0
is-wsl: ^2.2.0
mocha: ^9.2.2
node-fetch: ^3.2.5
undici: ^5.14.0
which-pm-runs: ^1.1.0
dependencies:
ci-info: 3.7.1
@ -3545,7 +3545,7 @@ importers:
dset: 3.1.2
is-docker: 3.0.0
is-wsl: 2.2.0
node-fetch: 3.3.0
undici: 5.14.0
which-pm-runs: 1.1.0
devDependencies:
'@types/debug': 4.1.7
@ -3575,16 +3575,15 @@ importers:
global-agent: ^3.0.0
magic-string: ^0.25.9
mocha: ^9.2.2
node-fetch: ^3.2.5
rollup: ^2.79.1
rollup-plugin-terser: ^7.0.2
tslib: ^2.4.0
typescript: ~4.7.3
undici: ^5.14.0
urlpattern-polyfill: ^1.0.0-rc5
web-streams-polyfill: ^3.2.1
dependencies:
global-agent: 3.0.0
node-fetch: 3.3.0
undici: 5.14.0
devDependencies:
'@rollup/plugin-alias': 3.1.9_rollup@2.79.1
'@rollup/plugin-inject': 4.0.4_rollup@2.79.1
@ -3607,7 +3606,6 @@ importers:
tslib: 2.4.1
typescript: 4.7.4
urlpattern-polyfill: 1.0.0-rc5
web-streams-polyfill: 3.2.1
scripts:
specifiers:
@ -7044,13 +7042,6 @@ packages:
'@types/unist': 2.0.6
dev: false
/@types/node-fetch/2.6.2:
resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==}
dependencies:
'@types/node': 18.11.18
form-data: 3.0.1
dev: true
/@types/node/12.20.55:
resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==}
dev: true
@ -8007,10 +7998,6 @@ packages:
resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==}
dev: false
/asynckit/0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
dev: true
/at-least-node/1.0.0:
resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
engines: {node: '>= 4.0.0'}
@ -8272,7 +8259,6 @@ packages:
engines: {node: '>=10.16.0'}
dependencies:
streamsearch: 1.1.0
dev: true
/bytes/3.1.2:
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
@ -8559,13 +8545,6 @@ packages:
resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==}
dev: false
/combined-stream/1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
dependencies:
delayed-stream: 1.0.0
dev: true
/comma-separated-tokens/2.0.3:
resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
dev: false
@ -8775,6 +8754,7 @@ packages:
/data-uri-to-buffer/4.0.0:
resolution: {integrity: sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==}
engines: {node: '>= 12'}
dev: false
/dataloader/1.4.0:
resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==}
@ -8927,11 +8907,6 @@ packages:
slash: 4.0.0
dev: true
/delayed-stream/1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
dev: true
/delegates/1.0.0:
resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
dev: false
@ -10094,15 +10069,6 @@ packages:
dependencies:
is-callable: 1.2.7
/form-data/3.0.1:
resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==}
engines: {node: '>= 6'}
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
mime-types: 2.1.35
dev: true
/format/0.2.2:
resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==}
engines: {node: '>=0.4.x'}
@ -12347,6 +12313,7 @@ packages:
data-uri-to-buffer: 4.0.0
fetch-blob: 3.2.0
formdata-polyfill: 4.0.10
dev: false
/node-forge/1.3.1:
resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==}
@ -14297,7 +14264,6 @@ packages:
/streamsearch/1.1.0:
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
engines: {node: '>=10.0.0'}
dev: true
/string-width/4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
@ -14978,6 +14944,12 @@ packages:
jiti: 1.16.1
dev: false
/undici/5.14.0:
resolution: {integrity: sha512-yJlHYw6yXPPsuOH0x2Ib1Km61vu4hLiRRQoafs+WUgX1vO64vgnxiCEN9dpIrhZyHFsai3F0AEj4P9zy19enEQ==}
engines: {node: '>=12.18'}
dependencies:
busboy: 1.6.0
/undici/5.9.1:
resolution: {integrity: sha512-6fB3a+SNnWEm4CJbgo0/CWR8RGcOCQP68SF4X0mxtYTq2VNN8T88NYrWVBAeSX+zb7bny2dx2iYhP3XHi00omg==}
engines: {node: '>=12.18'}