Skip to content

Commit b1e3795

Browse files
authored
feat: drop node16 (#1045)
* chore: drop structured-clone polyfill * ci: drop node16 * build: upgrade @types/node * build: drop blob-polyfill * build: drop undici * fix(vm): don't mutate headers * chore: remove getAll it was already deprecated * chore(node-utils): the default user agent is node it was "undici" in the past but no anymore https://github.com/nodejs/undici/blob/1b58a51e203d99cb8441c25044c8f8ed5bc63554/lib/web/fetch/index.js#L68C7-L68C23 * chore: use undici@6 * chore: drop esbuild alias * refactor: use esm syntax * chore: drop unnecessary crypto conditions * refactor: tweaks * test: remove polyfilledOrNative * build: adjust script * ci: use node22 * chore: drop Readable.fromWeb polyfill * chore: add changeset
1 parent 3d592c1 commit b1e3795

File tree

28 files changed

+5950
-4911
lines changed

28 files changed

+5950
-4911
lines changed

.changeset/thick-cooks-collect.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@edge-runtime/node-utils': major
3+
'@edge-runtime/primitives': major
4+
'@edge-runtime/vm': major
5+
---
6+
7+
drop node16

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ jobs:
1616
# https://nodejs.org/en/about/releases/
1717
# https://pnpm.io/installation#compatibility
1818
version:
19-
- node: 16
2019
- node: 18
2120
- node: 20
21+
- node: 22
2222

2323
name: Node.js ${{ matrix.version.node }}
2424
env:

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@
124124
"@jest/types": "29.5.0",
125125
"@svitejs/changesets-changelog-github-compact": "latest",
126126
"@types/jest": "29.5.14",
127-
"@types/node": "16",
127+
"@types/node": "18",
128128
"c8": "latest",
129129
"esbuild": "0.24.0",
130130
"finepack": "latest",

packages/integration-tests/test/abort-controller.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
import { polyfilledOrNative, guard } from './test-if'
2-
31
if (!globalThis.DOMException) {
42
globalThis.DOMException = require('@edge-runtime/ponyfill').DOMException
53
}
64

75
describe('AbortController', () => {
8-
guard(it, polyfilledOrNative)('allows to abort fetch', async () => {
6+
it('allows to abort fetch', async () => {
97
expect.assertions(1)
108
const controller = new AbortController()
119
controller.abort()

packages/integration-tests/test/blob.test.ts

Lines changed: 0 additions & 8 deletions
This file was deleted.

packages/integration-tests/test/body.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import { polyfilledOrNative, guard } from './test-if'
2-
3-
guard(describe, polyfilledOrNative)('body', () => {
1+
describe('body', () => {
42
test('throws when the body was directly consumed', async () => {
53
expect.assertions(9)
64

packages/integration-tests/test/crypto.test.ts

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { createHash } from 'crypto'
2-
import { polyfilledOrNative, guard } from './test-if'
32

43
if (!globalThis.crypto) {
54
globalThis.crypto = require('crypto')
@@ -15,20 +14,17 @@ test('crypto.randomUUID', async () => {
1514
expect(crypto.randomUUID()).toEqual(expect.stringMatching(/^[a-f0-9-]+$/))
1615
})
1716

18-
guard(test, polyfilledOrNative)(
19-
'crypto.subtle.digest returns a SHA-256 hash',
20-
async () => {
21-
const digest = await crypto.subtle.digest(
22-
'SHA-256',
23-
new Uint8Array([104, 105, 33]),
24-
)
25-
expect(toHex(digest)).toEqual(
26-
createHash('sha256').update('hi!').digest('hex'),
27-
)
28-
},
29-
)
17+
test('crypto.subtle.digest returns a SHA-256 hash', async () => {
18+
const digest = await crypto.subtle.digest(
19+
'SHA-256',
20+
new Uint8Array([104, 105, 33]),
21+
)
22+
expect(toHex(digest)).toEqual(
23+
createHash('sha256').update('hi!').digest('hex'),
24+
)
25+
})
3026

31-
guard(test, polyfilledOrNative)('Ed25519', async () => {
27+
test('Ed25519', async () => {
3228
const kp = await crypto.subtle.generateKey('Ed25519', false, [
3329
'sign',
3430
'verify',
@@ -37,7 +33,7 @@ guard(test, polyfilledOrNative)('Ed25519', async () => {
3733
expect(kp).toHaveProperty('publicKey')
3834
})
3935

40-
guard(test, polyfilledOrNative)('X25519', async () => {
36+
test('X25519', async () => {
4137
const kp = await crypto.subtle.generateKey('X25519', false, [
4238
'deriveBits',
4339
'deriveKey',

packages/integration-tests/test/fetch.test.ts

Lines changed: 80 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
import listen from 'test-listen'
88
import multer from 'multer'
99
import consume from 'stream/consumers'
10-
import { polyfilledOrNative, guard, isEdgeRuntime } from './test-if'
10+
import { guard, isEdgeRuntime } from './test-if'
1111
import { Duplex } from 'stream'
1212

1313
/**
@@ -43,7 +43,56 @@ afterEach(
4343
}),
4444
)
4545

46-
guard(describe, polyfilledOrNative())('fetch', () => {
46+
describe('fetch', () => {
47+
it.each(
48+
[
49+
isEdgeRuntime() && ['host', 'vercel.com'],
50+
['content-type', 'application/json'],
51+
['connection', 'keep-alive'],
52+
isEdgeRuntime() && ['keep-alive', 'timeout=5'],
53+
isEdgeRuntime() && ['upgrade', 'websocket'],
54+
].filter(Boolean) as [string, string][],
55+
)("sets '%s' header in the constructor", async (name, value) => {
56+
server = createServer(async (req: IncomingMessage, res: ServerResponse) => {
57+
res.setHeader('Content-Type', 'application/json')
58+
res.end(JSON.stringify(req.headers))
59+
})
60+
61+
const serverUrl = await listen(server)
62+
let response = await fetch(serverUrl, { headers: { [name]: value } })
63+
expect(response.status).toBe(200)
64+
let json = await response.json()
65+
expect(json[name]).toBe(value)
66+
67+
response = await fetch(
68+
new Request(serverUrl, { headers: { [name]: value } }),
69+
)
70+
expect(response.status).toBe(200)
71+
json = await response.json()
72+
expect(json[name]).toBe(value)
73+
74+
response = await fetch(
75+
new Request(serverUrl, { headers: new Headers({ [name]: value }) }),
76+
)
77+
expect(response.status).toBe(200)
78+
json = await response.json()
79+
expect(json[name]).toBe(value)
80+
81+
response = await fetch(
82+
new Request(serverUrl, { headers: new Headers({ [name]: value }) }),
83+
)
84+
expect(response.status).toBe(200)
85+
json = await response.json()
86+
expect(json[name]).toBe(value)
87+
88+
response = await fetch(
89+
new Request(serverUrl, { headers: { [name]: value } }),
90+
)
91+
expect(response.status).toBe(200)
92+
json = await response.json()
93+
expect(json[name]).toBe(value)
94+
})
95+
4796
test('perform a GET', async () => {
4897
server = createServer(async (req: IncomingMessage, res: ServerResponse) => {
4998
if (req.method !== 'GET') {
@@ -164,23 +213,7 @@ guard(describe, polyfilledOrNative())('fetch', () => {
164213
})
165214
})
166215

167-
test('sets header calling Headers constructor', async () => {
168-
server = createServer(async (req: IncomingMessage, res: ServerResponse) => {
169-
res.end(req.headers['user-agent'])
170-
})
171-
const serverUrl = await listen(server)
172-
const response = await fetch(serverUrl, {
173-
headers: new Headers({ 'user-agent': 'vercel/edge-runtime' }),
174-
})
175-
expect(response.status).toBe(200)
176-
const text = await response.text()
177-
expect(text).toBe('vercel/edge-runtime')
178-
})
179-
})
180-
181-
guard(test, polyfilledOrNative() && isEdgeRuntime())(
182-
'sets headers unsupported in undici',
183-
async () => {
216+
guard(it, isEdgeRuntime)('sets headers unsupported in undici', async () => {
184217
const url = new URL('/', 'https://example.vercel.sh')
185218
const response = await fetch(url, {
186219
headers: {
@@ -189,5 +222,31 @@ guard(test, polyfilledOrNative() && isEdgeRuntime())(
189222
},
190223
})
191224
expect(response.status).toBe(200)
192-
},
193-
)
225+
})
226+
227+
guard(it, isEdgeRuntime)(
228+
'sets header calling Headers constructor',
229+
async () => {
230+
server = createServer(
231+
async (req: IncomingMessage, res: ServerResponse) => {
232+
res.setHeader('Content-Type', 'application/json')
233+
res.end(JSON.stringify(req.headers))
234+
},
235+
)
236+
const serverUrl = await listen(server)
237+
const response = await fetch(serverUrl, {
238+
headers: new Headers({
239+
'user-agent': 'vercel/edge-runtime',
240+
host: 'example.com',
241+
'x-host': 'example.com',
242+
}),
243+
})
244+
expect(response.status).toBe(200)
245+
const headers = await response.json()
246+
247+
expect(headers['user-agent']).toBe('vercel/edge-runtime')
248+
expect(headers.host).toBe('example.com')
249+
expect(headers['x-host']).toBe('example.com')
250+
},
251+
)
252+
})
Lines changed: 54 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,68 @@
1-
import { guard, isEdgeRuntime, polyfilledOrNative } from './test-if'
1+
describe('headers', () => {
2+
it.each([
3+
['host', 'vercel.com'],
4+
['content-length', '1234'],
5+
['content-type', 'application/json'],
6+
['transfer-encoding', 'chunked'],
7+
['connection', 'keep-alive'],
8+
['keep-alive', 'timeout=5'],
9+
['upgrade', 'websocket'],
10+
['expect', '100-continue'],
11+
])("sets '%s' header in the constructor", async (name, value) => {
12+
const headers = new Headers({ [name]: value })
13+
expect(headers.get(name)).toBe(value)
14+
})
215

3-
guard(it, polyfilledOrNative)(
4-
'sets header calling Headers constructor',
5-
async () => {
16+
it('sets header calling Headers constructor', async () => {
617
const headers = new Headers()
718
headers.set('cookie', 'hello=world')
819
expect(headers.get('cookie')).toBe('hello=world')
9-
},
10-
)
11-
12-
guard(it, polyfilledOrNative)('multiple headers', async () => {
13-
const headers = new Headers()
14-
headers.append('set-cookie', 'foo=chocochip')
15-
headers.append('set-cookie', 'bar=chocochip')
16-
expect(headers.get('set-cookie')).toBe('foo=chocochip, bar=chocochip')
17-
expect([...headers]).toEqual([
18-
['set-cookie', 'foo=chocochip'],
19-
['set-cookie', 'bar=chocochip'],
20-
])
21-
})
22-
23-
guard(describe, isEdgeRuntime())('getAll', () => {
24-
test('on set-cookie', () => {
25-
const headers = new Headers()
26-
headers.append('set-cookie', 'a=1')
27-
headers.append('set-cookie', 'b=2')
28-
expect(headers.getSetCookie()).toEqual(['a=1', 'b=2'])
29-
expect(headers.getAll?.('set-cookie')).toEqual(['a=1', 'b=2'])
3020
})
3121

32-
test('on any other name', () => {
22+
it('multiple headers', async () => {
3323
const headers = new Headers()
34-
expect(() => headers.getAll?.('other')).toThrow(/getAll can only be used/)
24+
headers.append('set-cookie', 'foo=chocochip')
25+
headers.append('set-cookie', 'bar=chocochip')
26+
expect(headers.get('set-cookie')).toBe('foo=chocochip, bar=chocochip')
27+
expect([...headers]).toEqual([
28+
['set-cookie', 'foo=chocochip'],
29+
['set-cookie', 'bar=chocochip'],
30+
])
3531
})
36-
})
3732

38-
guard(describe, polyfilledOrNative)('iterators', () => {
39-
const generate = () => {
40-
const headers = new Headers()
41-
headers.append('a', '1')
42-
headers.append('b', '2')
43-
headers.append('set-cookie', 'c=3')
44-
headers.append('set-cookie', 'd=4')
45-
return headers
46-
}
33+
describe('iterators', () => {
34+
const generate = () => {
35+
const headers = new Headers()
36+
headers.append('a', '1')
37+
headers.append('b', '2')
38+
headers.append('set-cookie', 'c=3')
39+
headers.append('set-cookie', 'd=4')
40+
return headers
41+
}
4742

48-
test('#Symbol.iterator', () => {
49-
const entries = [...generate()]
50-
expect(entries).toEqual([
51-
['a', '1'],
52-
['b', '2'],
53-
['set-cookie', 'c=3'],
54-
['set-cookie', 'd=4'],
55-
])
56-
})
43+
test('#Symbol.iterator', () => {
44+
const entries = [...generate()]
45+
expect(entries).toEqual([
46+
['a', '1'],
47+
['b', '2'],
48+
['set-cookie', 'c=3'],
49+
['set-cookie', 'd=4'],
50+
])
51+
})
5752

58-
test('#entries', () => {
59-
const entries = [...generate().entries()]
60-
expect(entries).toEqual([
61-
['a', '1'],
62-
['b', '2'],
63-
['set-cookie', 'c=3'],
64-
['set-cookie', 'd=4'],
65-
])
66-
})
53+
test('#entries', () => {
54+
const entries = [...generate().entries()]
55+
expect(entries).toEqual([
56+
['a', '1'],
57+
['b', '2'],
58+
['set-cookie', 'c=3'],
59+
['set-cookie', 'd=4'],
60+
])
61+
})
6762

68-
test('#values', () => {
69-
const values = [...generate().values()]
70-
expect(values).toEqual(['1', '2', 'c=3', 'd=4'])
63+
test('#values', () => {
64+
const values = [...generate().values()]
65+
expect(values).toEqual(['1', '2', 'c=3', 'd=4'])
66+
})
7167
})
7268
})

packages/integration-tests/test/request.test.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,25 @@
1-
import { polyfilledOrNative, guard } from './test-if'
1+
describe('Request', () => {
2+
it.each([
3+
['host', 'vercel.com'],
4+
['content-length', '1234'],
5+
['content-type', 'application/json'],
6+
['transfer-encoding', 'chunked'],
7+
['connection', 'keep-alive'],
8+
['keep-alive', 'timeout=5'],
9+
['upgrade', 'websocket'],
10+
['expect', '100-continue'],
11+
])("sets '%s' header in the constructor", async (name, value) => {
12+
const headers = new Headers({ [name]: value })
13+
expect(
14+
new Request('https://vercel.com', { headers }).headers.get(name),
15+
).toBe(value)
16+
expect(
17+
new Request('https://vercel.com', {
18+
headers: { [name]: value },
19+
}).headers.get(name),
20+
).toBe(value)
21+
})
222

3-
guard(describe, polyfilledOrNative)('request', () => {
423
test('evaluate promise', () => {
524
const url = 'https://vercel.com/foo/bar?one=value'
625
const req = new Request(url)

0 commit comments

Comments
 (0)