diff --git a/package-lock.json b/package-lock.json
index 1ea38bf1e22..ce73c82cf2b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -81,7 +81,6 @@
"multiparty": "4.2.3",
"nanospinner": "1.2.2",
"netlify-redirector": "0.5.0",
- "node-fetch": "3.3.2",
"normalize-package-data": "7.0.1",
"open": "10.2.0",
"p-filter": "4.1.0",
diff --git a/package.json b/package.json
index 11e364d8c10..a4f834ef5db 100644
--- a/package.json
+++ b/package.json
@@ -128,7 +128,6 @@
"multiparty": "4.2.3",
"nanospinner": "1.2.2",
"netlify-redirector": "0.5.0",
- "node-fetch": "3.3.2",
"normalize-package-data": "7.0.1",
"open": "10.2.0",
"p-filter": "4.1.0",
diff --git a/src/commands/functions/functions-create.ts b/src/commands/functions/functions-create.ts
index 36f2bcec074..bead66ff354 100644
--- a/src/commands/functions/functions-create.ts
+++ b/src/commands/functions/functions-create.ts
@@ -10,7 +10,7 @@ import { OptionValues } from 'commander'
import { findUp } from 'find-up'
import fuzzy from 'fuzzy'
import inquirer from 'inquirer'
-import fetch from 'node-fetch'
+
import { createSpinner } from 'nanospinner'
import { fileExistsAsync } from '../../lib/fs.js'
@@ -386,7 +386,13 @@ const downloadFromURL = async function (command, options, argumentName, function
const res = await fetch(downloadUrl)
const finalName = path.basename(name, '.js') === functionName ? `${nameToUse}.js` : name
const dest = fs.createWriteStream(path.join(fnFolder, finalName))
- res.body?.pipe(dest)
+ if (res.body) {
+ for await (const chunk of res.body) {
+ dest.write(chunk)
+ }
+
+ dest.end()
+ }
} catch (error_) {
throw new Error(`Error while retrieving ${downloadUrl} ${error_}`)
}
diff --git a/src/commands/functions/functions-invoke.ts b/src/commands/functions/functions-invoke.ts
index 04a4788a9b4..77ab52f54e7 100644
--- a/src/commands/functions/functions-invoke.ts
+++ b/src/commands/functions/functions-invoke.ts
@@ -4,7 +4,6 @@ import path from 'path'
import { OptionValues } from 'commander'
import inquirer from 'inquirer'
-import fetch from 'node-fetch'
import { APIError, NETLIFYDEVWARN, chalk, logAndThrowError, exit } from '../../utils/command-helpers.js'
import { BACKGROUND, CLOCKWORK_USERAGENT, getFunctions } from '../../utils/functions/index.js'
diff --git a/src/lib/geo-location.ts b/src/lib/geo-location.ts
index 49ff1878cb4..b16b1f4428b 100644
--- a/src/lib/geo-location.ts
+++ b/src/lib/geo-location.ts
@@ -1,4 +1,3 @@
-import fetch from 'node-fetch'
import { type Geolocation, mockLocation } from '@netlify/dev-utils'
const API_URL = 'https://netlifind.netlify.app'
diff --git a/src/utils/live-tunnel.ts b/src/utils/live-tunnel.ts
index 4708bd41647..39918666deb 100644
--- a/src/utils/live-tunnel.ts
+++ b/src/utils/live-tunnel.ts
@@ -1,6 +1,5 @@
import { platform } from 'process'
-import fetch from 'node-fetch'
import pWaitFor from 'p-wait-for'
import { v4 as uuidv4 } from 'uuid'
diff --git a/src/utils/read-repo-url.ts b/src/utils/read-repo-url.ts
index 84fb40b7061..0793c74a110 100644
--- a/src/utils/read-repo-url.ts
+++ b/src/utils/read-repo-url.ts
@@ -1,7 +1,5 @@
import URL from 'url'
-import fetch from 'node-fetch'
-
// supported repo host types
const GITHUB = 'GitHub'
diff --git a/src/utils/sites/utils.ts b/src/utils/sites/utils.ts
index b60111a4b8a..75240df6307 100644
--- a/src/utils/sites/utils.ts
+++ b/src/utils/sites/utils.ts
@@ -1,4 +1,3 @@
-import fetch from 'node-fetch'
import execa from 'execa'
import { GitHubRepoResponse, logAndThrowError } from '../command-helpers.js'
diff --git a/src/utils/telemetry/request.ts b/src/utils/telemetry/request.ts
index e3c2bfb2b83..93481134642 100644
--- a/src/utils/telemetry/request.ts
+++ b/src/utils/telemetry/request.ts
@@ -2,8 +2,6 @@
// to run as a detached process
import process from 'process'
-import fetch from 'node-fetch'
-
import getPackageJson from '../get-cli-package-json.js'
const { name, version } = await getPackageJson()
diff --git a/tests/integration/commands/deploy/deploy.test.ts b/tests/integration/commands/deploy/deploy.test.ts
index f3ddcd489e2..8e95a16abab 100644
--- a/tests/integration/commands/deploy/deploy.test.ts
+++ b/tests/integration/commands/deploy/deploy.test.ts
@@ -4,7 +4,7 @@ import { fileURLToPath } from 'url'
import { load } from 'cheerio'
import execa from 'execa'
-import fetch from 'node-fetch'
+
import { afterAll, beforeAll, describe, expect, test } from 'vitest'
import { callCli } from '../../utils/call-cli.js'
diff --git a/tests/integration/commands/dev/dev-forms-and-redirects.test.ts b/tests/integration/commands/dev/dev-forms-and-redirects.test.ts
index 12e6d0726ac..3b73f734284 100644
--- a/tests/integration/commands/dev/dev-forms-and-redirects.test.ts
+++ b/tests/integration/commands/dev/dev-forms-and-redirects.test.ts
@@ -4,9 +4,8 @@ import path from 'path'
import type { HandlerEvent } from '@netlify/functions'
import js from 'dedent'
-import FormData from 'form-data'
import getPort from 'get-port'
-import fetch from 'node-fetch'
+
import { describe, test } from 'vitest'
import { withDevServer } from '../../utils/dev-server.js'
@@ -113,7 +112,7 @@ describe.concurrent('commands/dev-forms-and-redirects', () => {
data: {
ip: '::ffff:127.0.0.1',
some: 'thing',
- user_agent: 'node-fetch',
+ user_agent: 'node',
},
human_fields: {
Some: 'thing',
@@ -316,7 +315,7 @@ describe.concurrent('commands/dev-forms-and-redirects', () => {
await builder.build()
await withDevServer({ cwd: builder.directory }, async (server) => {
- const response = await fetch(`${server.url}/foo.html`, { follow: 0 })
+ const response = await fetch(`${server.url}/foo.html`, { redirect: 'manual' })
t.expect(response).not.toHaveProperty('headers.location')
t.expect(await response.text()).toEqual('
foo')
})
diff --git a/tests/integration/commands/dev/dev-miscellaneous.test.ts b/tests/integration/commands/dev/dev-miscellaneous.test.ts
index 709645b353c..90107e37b83 100644
--- a/tests/integration/commands/dev/dev-miscellaneous.test.ts
+++ b/tests/integration/commands/dev/dev-miscellaneous.test.ts
@@ -1,4 +1,3 @@
-import events from 'node:events'
import { Buffer } from 'buffer'
import path from 'path'
import { platform } from 'process'
@@ -8,7 +7,7 @@ import { setProperty } from 'dot-prop'
import execa, { ExecaError } from 'execa'
import getAvailablePort from 'get-port'
import jwt from 'jsonwebtoken'
-import fetch from 'node-fetch'
+
import { type TestContext, describe, test } from 'vitest'
import type { HandlerEvent, HandlerContext } from '@netlify/functions'
import type { Context as EdgeHandlerContext } from '@netlify/edge-functions'
@@ -595,12 +594,17 @@ describe.concurrent('commands/dev-miscellaneous', () => {
const stream = res.body
expect(stream).not.toBeNull()
+ if (!stream) throw new Error('Expected a readable stream')
let numberOfChunks = 0
- stream!.on('data', () => {
+
+ const reader = stream.getReader()
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+ while (true) {
+ const { done } = await reader.read()
+ if (done) break
numberOfChunks += 1
- })
- await events.once(stream!, 'end')
+ }
// streamed responses arrive in more than one batch
expect(numberOfChunks).not.toBe(1)
diff --git a/tests/integration/commands/dev/dev.config.test.ts b/tests/integration/commands/dev/dev.config.test.ts
index e6aa6984b68..08cdf6ab1b0 100644
--- a/tests/integration/commands/dev/dev.config.test.ts
+++ b/tests/integration/commands/dev/dev.config.test.ts
@@ -1,11 +1,9 @@
import { Buffer } from 'node:buffer'
import { version } from 'node:process'
-import events from 'node:events'
import type { HandlerEvent } from '@netlify/functions'
-import FormData from 'form-data'
import getPort from 'get-port'
-import fetch from 'node-fetch'
+
import { gte } from 'semver'
import { describe, test } from 'vitest'
@@ -437,9 +435,8 @@ describe.concurrent('commands/dev/config', () => {
await withDevServer({ cwd: builder.directory }, async (server) => {
const form = new FormData()
form.append('some', 'thing')
-
- const expectedBoundary = form.getBoundary()
- const expectedResponseBody = form.getBuffer().toString('base64')
+ const rsp = new Response(form)
+ const expectedResponseBody = Buffer.from(await rsp.text()).toString('base64')
const response = await fetch(`${server.url}/api/echo?ding=dong`, {
method: 'POST',
@@ -448,8 +445,10 @@ describe.concurrent('commands/dev/config', () => {
const body = await response.json()
t.expect(body).toHaveProperty('headers.host', `${server.host}:${server.port.toString()}`)
- t.expect(body).toHaveProperty('headers.content-type', `multipart/form-data;boundary=${expectedBoundary}`)
- t.expect(body).toHaveProperty('headers.content-length', '164')
+ t.expect((body as { headers: { 'content-type': string } }).headers['content-type']).toMatch(
+ /^multipart\/form-data; ?boundary=.+/,
+ )
+ t.expect(body).toHaveProperty('headers.content-length', '126')
t.expect(body).toHaveProperty('httpMethod', 'POST')
t.expect(body).toHaveProperty('isBase64Encoded', true)
t.expect(body).toHaveProperty('path', '/api/echo')
@@ -521,15 +520,12 @@ describe.concurrent('commands/dev/config', () => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const body = res.body!
- body.on('data', (chunk: Buffer) => {
+ for await (const chunk of body) {
const now = Date.now()
-
t.expect(now > lastTimestamp).toBe(true)
-
lastTimestamp = now
- chunks.push(chunk.toString())
- })
- await events.once(body, 'end')
+ chunks.push(Buffer.from(chunk).toString())
+ }
t.expect(chunks).toStrictEqual(['one', 'two', 'three'])
})
diff --git a/tests/integration/commands/dev/dev.test.ts b/tests/integration/commands/dev/dev.test.ts
index 498a2be85cc..be38c864974 100644
--- a/tests/integration/commands/dev/dev.test.ts
+++ b/tests/integration/commands/dev/dev.test.ts
@@ -6,7 +6,7 @@ import process from 'process'
import js from 'dedent'
import jwt, { type JwtPayload } from 'jsonwebtoken'
-import fetch from 'node-fetch'
+
import { describe, test } from 'vitest'
import { withDevServer } from '../../utils/dev-server.js'
@@ -243,7 +243,7 @@ describe.concurrent('command/dev', () => {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'param=value',
- follow: 0,
+ redirect: 'manual',
})
const postBody = await postResponse.json()
t.expect(postBody).toHaveProperty('body', { param: 'value' })
@@ -311,7 +311,7 @@ describe.concurrent('command/dev', () => {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'param=value',
- follow: 0,
+ redirect: 'manual',
}),
])
const getBody = await getResponse.json()
@@ -348,7 +348,7 @@ describe.concurrent('command/dev', () => {
await withDevServer({ cwd: builder.directory }, async (server) => {
const [response1, response2] = await Promise.all([
- fetch(`${server.url}/api/ping`, { follow: 0, redirect: 'manual' }),
+ fetch(`${server.url}/api/ping`, { redirect: 'manual' }),
fetch(`${server.url}/api/ping`),
])
const response2Body = await response2.json()
@@ -417,7 +417,7 @@ describe.concurrent('command/dev', () => {
const response = await fetch(`${server.url}/api/echo`, {
method: 'POST',
body: 'param=value',
- follow: 0,
+ redirect: 'manual',
})
// Method Not Allowed
diff --git a/tests/integration/commands/dev/edge-functions.test.ts b/tests/integration/commands/dev/edge-functions.test.ts
index ab4683a8423..107ba7fdd6f 100644
--- a/tests/integration/commands/dev/edge-functions.test.ts
+++ b/tests/integration/commands/dev/edge-functions.test.ts
@@ -3,7 +3,7 @@ import { rename } from 'fs/promises'
import { join } from 'path'
import execa from 'execa'
-import fetch from 'node-fetch'
+
import { describe, expect, test } from 'vitest'
import { withDevServer } from '../../utils/dev-server.js'
diff --git a/tests/integration/commands/dev/images.test.ts b/tests/integration/commands/dev/images.test.ts
index 57e5a82e2aa..ff19626bd96 100644
--- a/tests/integration/commands/dev/images.test.ts
+++ b/tests/integration/commands/dev/images.test.ts
@@ -2,7 +2,6 @@
import fs from 'fs'
import path from 'path'
-import fetch from 'node-fetch'
import { describe, test } from 'vitest'
import { withDevServer } from '../../utils/dev-server.js'
diff --git a/tests/integration/commands/dev/redirects.test.ts b/tests/integration/commands/dev/redirects.test.ts
index 74f3315ee9e..d122f54d58f 100644
--- a/tests/integration/commands/dev/redirects.test.ts
+++ b/tests/integration/commands/dev/redirects.test.ts
@@ -1,4 +1,3 @@
-import fetch from 'node-fetch'
import { describe, expect, test } from 'vitest'
import { withDevServer } from '../../utils/dev-server.js'
diff --git a/tests/integration/commands/dev/responses.dev.test.ts b/tests/integration/commands/dev/responses.dev.test.ts
index 3ac82e477e7..519a6795cd5 100644
--- a/tests/integration/commands/dev/responses.dev.test.ts
+++ b/tests/integration/commands/dev/responses.dev.test.ts
@@ -2,7 +2,7 @@
import path from 'path'
import type { HandlerEvent } from '@netlify/functions'
-import fetch from 'node-fetch'
+
import { describe, test } from 'vitest'
import { withDevServer } from '../../utils/dev-server.js'
diff --git a/tests/integration/commands/dev/scheduled-functions.test.ts b/tests/integration/commands/dev/scheduled-functions.test.ts
index d2063cf48d4..ce90edcfbd9 100644
--- a/tests/integration/commands/dev/scheduled-functions.test.ts
+++ b/tests/integration/commands/dev/scheduled-functions.test.ts
@@ -1,7 +1,7 @@
import { describe, expect, test } from 'vitest'
import { FixtureTestContext, setupFixtureTests } from '../../utils/fixture.js'
-import fetch from 'node-fetch'
+
import { pause } from '../../utils/pause.js'
describe('scheduled functions', async () => {
diff --git a/tests/integration/commands/functions-invoke/functions-invoke.test.ts b/tests/integration/commands/functions-invoke/functions-invoke.test.ts
index 206ab20fbab..88f8e5f1e9a 100644
--- a/tests/integration/commands/functions-invoke/functions-invoke.test.ts
+++ b/tests/integration/commands/functions-invoke/functions-invoke.test.ts
@@ -1,4 +1,3 @@
-import fetch from 'node-fetch'
import { describe, test } from 'vitest'
import { FixtureTestContext, setupFixtureTests } from '../../utils/fixture.js'
diff --git a/tests/integration/commands/functions-serve/functions-serve.test.ts b/tests/integration/commands/functions-serve/functions-serve.test.ts
index d18f00fe775..2ac2182668b 100644
--- a/tests/integration/commands/functions-serve/functions-serve.test.ts
+++ b/tests/integration/commands/functions-serve/functions-serve.test.ts
@@ -2,7 +2,7 @@ import { killProcess } from '@netlify/dev-utils'
import js from 'dedent'
import execa from 'execa'
import getPort from 'get-port'
-import fetch from 'node-fetch'
+
import { describe, test } from 'vitest'
import waitPort from 'wait-port'
diff --git a/tests/integration/commands/functions-with-args/functions-with-args.test.ts b/tests/integration/commands/functions-with-args/functions-with-args.test.ts
index 8279b23aa01..4854bc4b3ca 100644
--- a/tests/integration/commands/functions-with-args/functions-with-args.test.ts
+++ b/tests/integration/commands/functions-with-args/functions-with-args.test.ts
@@ -2,7 +2,7 @@ import path from 'node:path'
import { fileURLToPath } from 'node:url'
import type { HandlerEvent } from '@netlify/functions'
-import fetch from 'node-fetch'
+
import { describe, test } from 'vitest'
import js from 'dedent'
import ts from 'dedent'
diff --git a/tests/integration/framework-detection.test.ts b/tests/integration/framework-detection.test.ts
index fe32530e507..a6ca693999a 100644
--- a/tests/integration/framework-detection.test.ts
+++ b/tests/integration/framework-detection.test.ts
@@ -1,5 +1,5 @@
import execa from 'execa'
-import fetch from 'node-fetch'
+
import { describe, test } from 'vitest'
import { cliPath } from './utils/cli-path.js'
diff --git a/tests/integration/frameworks/hugo.test.ts b/tests/integration/frameworks/hugo.test.ts
index d0bb720c6dc..b1921e7a265 100644
--- a/tests/integration/frameworks/hugo.test.ts
+++ b/tests/integration/frameworks/hugo.test.ts
@@ -1,7 +1,6 @@
import { expect, test } from 'vitest'
import { FixtureTestContext, setupFixtureTests } from '../utils/fixture.js'
-import fetch from 'node-fetch'
setupFixtureTests('hugo-site', { devServer: true }, () => {
test('should not infinite redirect when -d flag is passed', async ({ devServer }) => {
diff --git a/tests/integration/rules-proxy.test.ts b/tests/integration/rules-proxy.test.ts
index e70e76df639..c4247c02d22 100644
--- a/tests/integration/rules-proxy.test.ts
+++ b/tests/integration/rules-proxy.test.ts
@@ -6,7 +6,6 @@ import { afterAll, beforeAll, describe, expect, test } from 'vitest'
import { createRewriter, getWatchers } from '../../src/utils/rules-proxy.js'
-import fetch from 'node-fetch'
import { createSiteBuilder, SiteBuilder } from './utils/site-builder.js'
describe('rules-proxy', () => {
diff --git a/tests/integration/serve/functions-go.test.ts b/tests/integration/serve/functions-go.test.ts
index 9126467ca21..96526536b89 100644
--- a/tests/integration/serve/functions-go.test.ts
+++ b/tests/integration/serve/functions-go.test.ts
@@ -1,4 +1,3 @@
-import fetch from 'node-fetch'
import { describe, test } from 'vitest'
import { tryAndLogOutput, withDevServer } from '../utils/dev-server.js'
diff --git a/tests/integration/serve/functions-rust.test.ts b/tests/integration/serve/functions-rust.test.ts
index bc16040acfe..f8644e336d4 100644
--- a/tests/integration/serve/functions-rust.test.ts
+++ b/tests/integration/serve/functions-rust.test.ts
@@ -1,4 +1,3 @@
-import fetch from 'node-fetch'
import { test } from 'vitest'
import { tryAndLogOutput, withDevServer } from '../utils/dev-server.js'
diff --git a/tests/unit/lib/functions/server.test.ts b/tests/unit/lib/functions/server.test.ts
index fd3ba16e8d0..4371eea0806 100644
--- a/tests/unit/lib/functions/server.test.ts
+++ b/tests/unit/lib/functions/server.test.ts
@@ -6,7 +6,7 @@ import { join } from 'node:path'
import { LocalState } from '@netlify/dev-utils'
import express from 'express'
-import fetch from 'node-fetch'
+
import { afterAll, beforeAll, describe, expect, test, vi } from 'vitest'
import { FunctionsRegistry } from '../../../../src/lib/functions/registry.js'
diff --git a/tests/unit/utils/gh-auth.test.ts b/tests/unit/utils/gh-auth.test.ts
index 1186308ee8a..4a5fa294e80 100644
--- a/tests/unit/utils/gh-auth.test.ts
+++ b/tests/unit/utils/gh-auth.test.ts
@@ -1,5 +1,5 @@
import { fibonacci } from 'backoff'
-import fetch from 'node-fetch'
+
import { afterAll, describe, expect, test, vi } from 'vitest'
import { authWithNetlify } from '../../../src/utils/gh-auth.js'