Skip to content

Commit 0a8e69e

Browse files
Merge branch 'main' into fix/transport-close-stack-overflow
2 parents ff06d69 + cc9c9d1 commit 0a8e69e

8 files changed

Lines changed: 201 additions & 13 deletions

File tree

.github/dependabot.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: 'github-actions'
4+
directory: '/'
5+
schedule:
6+
interval: 'weekly'

.github/workflows/conformance.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
steps:
2121
- uses: actions/checkout@v6
2222
- name: Install pnpm
23-
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
23+
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
2424
with:
2525
run_install: false
2626
- uses: actions/setup-node@v6
@@ -38,7 +38,7 @@ jobs:
3838
steps:
3939
- uses: actions/checkout@v6
4040
- name: Install pnpm
41-
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
41+
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
4242
with:
4343
run_install: false
4444
- uses: actions/setup-node@v6

.github/workflows/deploy-docs.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
- uses: actions/checkout@v6
2929

3030
- name: Install pnpm
31-
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
31+
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
3232
with:
3333
run_install: false
3434
- uses: actions/setup-node@v6
@@ -41,13 +41,13 @@ jobs:
4141
run: bash scripts/generate-multidoc.sh tmp/docs-combined
4242

4343
- name: Configure Pages
44-
uses: actions/configure-pages@v5
44+
uses: actions/configure-pages@v6
4545

4646
- name: Upload Pages artifact
47-
uses: actions/upload-pages-artifact@v3
47+
uses: actions/upload-pages-artifact@v4
4848
with:
4949
path: tmp/docs-combined
5050

5151
- name: Deploy to GitHub Pages
5252
id: deployment
53-
uses: actions/deploy-pages@v4
53+
uses: actions/deploy-pages@v5

.github/workflows/main.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
- uses: actions/checkout@v6
2020

2121
- name: Install pnpm
22-
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
22+
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
2323
id: pnpm-install
2424
with:
2525
run_install: false
@@ -44,7 +44,7 @@ jobs:
4444
- uses: actions/checkout@v6
4545

4646
- name: Install pnpm
47-
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
47+
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
4848
id: pnpm-install
4949
with:
5050
run_install: false
@@ -71,7 +71,7 @@ jobs:
7171
steps:
7272
- uses: actions/checkout@v6
7373
- name: Install pnpm
74-
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
74+
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
7575
with:
7676
run_install: false
7777
- uses: actions/setup-node@v6
@@ -108,7 +108,7 @@ jobs:
108108
- uses: actions/checkout@v6
109109

110110
- name: Install pnpm
111-
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
111+
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
112112
id: pnpm-install
113113
with:
114114
run_install: false

.github/workflows/publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
- uses: actions/checkout@v6
2020

2121
- name: Install pnpm
22-
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
22+
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
2323
with:
2424
run_install: false
2525

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
- uses: actions/checkout@v6
2020

2121
- name: Install pnpm
22-
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
22+
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
2323
with:
2424
run_install: false
2525

.github/workflows/update-spec-types.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
uses: actions/checkout@v6
1919

2020
- name: Install pnpm
21-
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
21+
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
2222
id: pnpm-install
2323
with:
2424
run_install: false
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
import { createFetchWithInit, type FetchLike, normalizeHeaders } from '../../src/shared/transport.js';
2+
3+
describe('normalizeHeaders', () => {
4+
test('returns empty object for undefined', () => {
5+
expect(normalizeHeaders(undefined)).toEqual({});
6+
});
7+
8+
test('handles Headers instance', () => {
9+
const headers = new Headers({
10+
'x-foo': 'bar',
11+
'content-type': 'application/json'
12+
});
13+
expect(normalizeHeaders(headers)).toEqual({
14+
'x-foo': 'bar',
15+
'content-type': 'application/json'
16+
});
17+
});
18+
19+
test('handles array of tuples', () => {
20+
const headers: [string, string][] = [
21+
['x-foo', 'bar'],
22+
['x-baz', 'qux']
23+
];
24+
expect(normalizeHeaders(headers)).toEqual({
25+
'x-foo': 'bar',
26+
'x-baz': 'qux'
27+
});
28+
});
29+
30+
test('handles plain object', () => {
31+
const headers = { 'x-foo': 'bar', 'x-baz': 'qux' };
32+
expect(normalizeHeaders(headers)).toEqual({
33+
'x-foo': 'bar',
34+
'x-baz': 'qux'
35+
});
36+
});
37+
38+
test('returns a shallow copy for plain objects', () => {
39+
const headers = { 'x-foo': 'bar' };
40+
const result = normalizeHeaders(headers);
41+
expect(result).not.toBe(headers);
42+
expect(result).toEqual(headers);
43+
});
44+
});
45+
46+
describe('createFetchWithInit', () => {
47+
test('returns baseFetch unchanged when no baseInit provided', () => {
48+
const mockFetch: FetchLike = vi.fn();
49+
const result = createFetchWithInit(mockFetch);
50+
expect(result).toBe(mockFetch);
51+
});
52+
53+
test('passes baseInit to fetch when no call init provided', async () => {
54+
const mockFetch: FetchLike = vi.fn();
55+
const baseInit: RequestInit = {
56+
method: 'POST',
57+
credentials: 'include'
58+
};
59+
60+
const wrappedFetch = createFetchWithInit(mockFetch, baseInit);
61+
await wrappedFetch('https://example.com');
62+
63+
expect(mockFetch).toHaveBeenCalledWith(
64+
'https://example.com',
65+
expect.objectContaining({
66+
method: 'POST',
67+
credentials: 'include'
68+
})
69+
);
70+
});
71+
72+
test('merges baseInit with call init, call init wins for non-header fields', async () => {
73+
const mockFetch: FetchLike = vi.fn();
74+
const baseInit: RequestInit = {
75+
method: 'POST',
76+
credentials: 'include'
77+
};
78+
79+
const wrappedFetch = createFetchWithInit(mockFetch, baseInit);
80+
await wrappedFetch('https://example.com', { method: 'PUT' });
81+
82+
expect(mockFetch).toHaveBeenCalledWith(
83+
'https://example.com',
84+
expect.objectContaining({
85+
method: 'PUT',
86+
credentials: 'include'
87+
})
88+
);
89+
});
90+
91+
test('merges headers from both base and call init', async () => {
92+
const mockFetch: FetchLike = vi.fn();
93+
const baseInit: RequestInit = {
94+
headers: { 'x-base': 'base-value', 'x-shared': 'base' }
95+
};
96+
97+
const wrappedFetch = createFetchWithInit(mockFetch, baseInit);
98+
await wrappedFetch('https://example.com', {
99+
headers: { 'x-call': 'call-value', 'x-shared': 'call' }
100+
});
101+
102+
expect(mockFetch).toHaveBeenCalledWith(
103+
'https://example.com',
104+
expect.objectContaining({
105+
headers: {
106+
'x-base': 'base-value',
107+
'x-call': 'call-value',
108+
'x-shared': 'call'
109+
}
110+
})
111+
);
112+
});
113+
114+
test('uses baseInit headers when call init has no headers', async () => {
115+
const mockFetch: FetchLike = vi.fn();
116+
const baseInit: RequestInit = {
117+
headers: { 'x-base': 'base-value' }
118+
};
119+
120+
const wrappedFetch = createFetchWithInit(mockFetch, baseInit);
121+
await wrappedFetch('https://example.com', { method: 'POST' });
122+
123+
expect(mockFetch).toHaveBeenCalledWith(
124+
'https://example.com',
125+
expect.objectContaining({
126+
method: 'POST',
127+
headers: { 'x-base': 'base-value' }
128+
})
129+
);
130+
});
131+
132+
test('handles URL object as first argument', async () => {
133+
const mockFetch: FetchLike = vi.fn();
134+
const baseInit: RequestInit = { method: 'GET' };
135+
136+
const wrappedFetch = createFetchWithInit(mockFetch, baseInit);
137+
const url = new URL('https://example.com/path');
138+
await wrappedFetch(url);
139+
140+
expect(mockFetch).toHaveBeenCalledWith(url, expect.objectContaining({ method: 'GET' }));
141+
});
142+
143+
test('passes all baseInit properties when call init is empty object', async () => {
144+
const mockFetch: FetchLike = vi.fn();
145+
const baseInit: RequestInit = {
146+
method: 'POST',
147+
credentials: 'include',
148+
headers: { 'x-base': 'value' }
149+
};
150+
151+
const wrappedFetch = createFetchWithInit(mockFetch, baseInit);
152+
await wrappedFetch('https://example.com', {});
153+
154+
expect(mockFetch).toHaveBeenCalledWith(
155+
'https://example.com',
156+
expect.objectContaining({
157+
method: 'POST',
158+
credentials: 'include',
159+
headers: { 'x-base': 'value' }
160+
})
161+
);
162+
});
163+
164+
test('passes Headers instance through when call init has no headers', async () => {
165+
const mockFetch: FetchLike = vi.fn();
166+
const baseHeaders = new Headers({ 'x-base': 'value' });
167+
const baseInit: RequestInit = {
168+
headers: baseHeaders
169+
};
170+
171+
const wrappedFetch = createFetchWithInit(mockFetch, baseInit);
172+
await wrappedFetch('https://example.com', { method: 'POST' });
173+
174+
expect(mockFetch).toHaveBeenCalledWith(
175+
'https://example.com',
176+
expect.objectContaining({
177+
method: 'POST',
178+
headers: baseHeaders
179+
})
180+
);
181+
});
182+
});

0 commit comments

Comments
 (0)