@pikku/fetch 0.12.3 → 0.12.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/cjs/core-pikku-fetch.d.ts +2 -0
- package/dist/cjs/core-pikku-fetch.js +13 -3
- package/dist/esm/core-pikku-fetch.d.ts +2 -0
- package/dist/esm/core-pikku-fetch.js +13 -0
- package/package.json +1 -1
- package/src/core-pikku-fetch.ts +12 -0
- package/src/index.test.ts +44 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## 0.12.5
|
|
2
|
+
|
|
3
|
+
### Patch Changes
|
|
4
|
+
|
|
5
|
+
- 6f06813: Add `CorePikkuFetch.setHeader(name, value)` to set or clear an arbitrary request header (passing `null` removes it). Enables per-client headers such as admin impersonation (`x-pikku-impersonate-user-id`) without subclassing the fetch client.
|
|
6
|
+
|
|
7
|
+
## 0.12.4
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- ade6f0b: `subscribeToSSE`: call `onError` when the stream closes cleanly without the caller having called `close()`. Previously a server-side connection drop (or any clean EOF before the terminal event) exited the read loop silently, leaving the caller's `runPhase` stuck at `'running'` indefinitely with no way to recover.
|
|
12
|
+
|
|
1
13
|
## 0.12.3
|
|
2
14
|
|
|
3
15
|
### Patch Changes
|
|
@@ -27,6 +27,7 @@ export type CorePikkuFetchOptions = {
|
|
|
27
27
|
export declare class CorePikkuFetch {
|
|
28
28
|
private options;
|
|
29
29
|
private authHeaders;
|
|
30
|
+
private extraHeaders;
|
|
30
31
|
/**
|
|
31
32
|
* Constructs a new instance of the `CorePikkuFetch` class.
|
|
32
33
|
*
|
|
@@ -39,6 +40,7 @@ export declare class CorePikkuFetch {
|
|
|
39
40
|
* @returns {Record<string, string>} - The headers for the request.
|
|
40
41
|
*/
|
|
41
42
|
private getHeaders;
|
|
43
|
+
setHeader(name: string, value: string | null): void;
|
|
42
44
|
/**
|
|
43
45
|
* Sets the server URL for subsequent requests.
|
|
44
46
|
*
|
|
@@ -26,6 +26,7 @@ class CorePikkuFetch {
|
|
|
26
26
|
constructor(options = {}) {
|
|
27
27
|
this.options = options;
|
|
28
28
|
this.authHeaders = {};
|
|
29
|
+
this.extraHeaders = {};
|
|
29
30
|
this.authHeaders = options.authHeaders || {};
|
|
30
31
|
}
|
|
31
32
|
/**
|
|
@@ -34,9 +35,7 @@ class CorePikkuFetch {
|
|
|
34
35
|
* @returns {Record<string, string>} - The headers for the request.
|
|
35
36
|
*/
|
|
36
37
|
getHeaders() {
|
|
37
|
-
const headers = {
|
|
38
|
-
'Content-Type': 'application/json',
|
|
39
|
-
};
|
|
38
|
+
const headers = Object.assign({ 'Content-Type': 'application/json' }, this.extraHeaders);
|
|
40
39
|
if (this.authHeaders.jwt) {
|
|
41
40
|
headers.Authorization = `Bearer ${this.authHeaders.jwt}`;
|
|
42
41
|
}
|
|
@@ -45,6 +44,14 @@ class CorePikkuFetch {
|
|
|
45
44
|
}
|
|
46
45
|
return headers;
|
|
47
46
|
}
|
|
47
|
+
setHeader(name, value) {
|
|
48
|
+
if (value === null) {
|
|
49
|
+
delete this.extraHeaders[name];
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
this.extraHeaders[name] = value;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
48
55
|
/**
|
|
49
56
|
* Sets the server URL for subsequent requests.
|
|
50
57
|
*
|
|
@@ -224,6 +231,9 @@ class CorePikkuFetch {
|
|
|
224
231
|
handler(parsed);
|
|
225
232
|
}
|
|
226
233
|
}
|
|
234
|
+
// Clean EOF before caller called close() = unexpected stream termination.
|
|
235
|
+
if (!closed)
|
|
236
|
+
throw new Error('SSE stream closed unexpectedly');
|
|
227
237
|
}
|
|
228
238
|
catch (err) {
|
|
229
239
|
if (!closed)
|
|
@@ -27,6 +27,7 @@ export type CorePikkuFetchOptions = {
|
|
|
27
27
|
export declare class CorePikkuFetch {
|
|
28
28
|
private options;
|
|
29
29
|
private authHeaders;
|
|
30
|
+
private extraHeaders;
|
|
30
31
|
/**
|
|
31
32
|
* Constructs a new instance of the `CorePikkuFetch` class.
|
|
32
33
|
*
|
|
@@ -39,6 +40,7 @@ export declare class CorePikkuFetch {
|
|
|
39
40
|
* @returns {Record<string, string>} - The headers for the request.
|
|
40
41
|
*/
|
|
41
42
|
private getHeaders;
|
|
43
|
+
setHeader(name: string, value: string | null): void;
|
|
42
44
|
/**
|
|
43
45
|
* Sets the server URL for subsequent requests.
|
|
44
46
|
*
|
|
@@ -8,6 +8,7 @@ import { corePikkuFetch } from './pikku-fetch.js';
|
|
|
8
8
|
export class CorePikkuFetch {
|
|
9
9
|
options;
|
|
10
10
|
authHeaders = {};
|
|
11
|
+
extraHeaders = {};
|
|
11
12
|
/**
|
|
12
13
|
* Constructs a new instance of the `CorePikkuFetch` class.
|
|
13
14
|
*
|
|
@@ -25,6 +26,7 @@ export class CorePikkuFetch {
|
|
|
25
26
|
getHeaders() {
|
|
26
27
|
const headers = {
|
|
27
28
|
'Content-Type': 'application/json',
|
|
29
|
+
...this.extraHeaders,
|
|
28
30
|
};
|
|
29
31
|
if (this.authHeaders.jwt) {
|
|
30
32
|
headers.Authorization = `Bearer ${this.authHeaders.jwt}`;
|
|
@@ -34,6 +36,14 @@ export class CorePikkuFetch {
|
|
|
34
36
|
}
|
|
35
37
|
return headers;
|
|
36
38
|
}
|
|
39
|
+
setHeader(name, value) {
|
|
40
|
+
if (value === null) {
|
|
41
|
+
delete this.extraHeaders[name];
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
this.extraHeaders[name] = value;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
37
47
|
/**
|
|
38
48
|
* Sets the server URL for subsequent requests.
|
|
39
49
|
*
|
|
@@ -202,6 +212,9 @@ export class CorePikkuFetch {
|
|
|
202
212
|
handler(parsed);
|
|
203
213
|
}
|
|
204
214
|
}
|
|
215
|
+
// Clean EOF before caller called close() = unexpected stream termination.
|
|
216
|
+
if (!closed)
|
|
217
|
+
throw new Error('SSE stream closed unexpectedly');
|
|
205
218
|
}
|
|
206
219
|
catch (err) {
|
|
207
220
|
if (!closed)
|
package/package.json
CHANGED
package/src/core-pikku-fetch.ts
CHANGED
|
@@ -32,6 +32,7 @@ export type CorePikkuFetchOptions = {
|
|
|
32
32
|
*/
|
|
33
33
|
export class CorePikkuFetch {
|
|
34
34
|
private authHeaders: AuthHeaders = {}
|
|
35
|
+
private extraHeaders: Record<string, string> = {}
|
|
35
36
|
|
|
36
37
|
/**
|
|
37
38
|
* Constructs a new instance of the `CorePikkuFetch` class.
|
|
@@ -50,6 +51,7 @@ export class CorePikkuFetch {
|
|
|
50
51
|
private getHeaders(): Record<string, string> {
|
|
51
52
|
const headers: Record<string, string> = {
|
|
52
53
|
'Content-Type': 'application/json',
|
|
54
|
+
...this.extraHeaders,
|
|
53
55
|
}
|
|
54
56
|
if (this.authHeaders.jwt) {
|
|
55
57
|
headers.Authorization = `Bearer ${this.authHeaders.jwt}`
|
|
@@ -59,6 +61,14 @@ export class CorePikkuFetch {
|
|
|
59
61
|
return headers
|
|
60
62
|
}
|
|
61
63
|
|
|
64
|
+
public setHeader(name: string, value: string | null): void {
|
|
65
|
+
if (value === null) {
|
|
66
|
+
delete this.extraHeaders[name]
|
|
67
|
+
} else {
|
|
68
|
+
this.extraHeaders[name] = value
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
62
72
|
/**
|
|
63
73
|
* Sets the server URL for subsequent requests.
|
|
64
74
|
*
|
|
@@ -254,6 +264,8 @@ export class CorePikkuFetch {
|
|
|
254
264
|
handler(parsed)
|
|
255
265
|
}
|
|
256
266
|
}
|
|
267
|
+
// Clean EOF before caller called close() = unexpected stream termination.
|
|
268
|
+
if (!closed) throw new Error('SSE stream closed unexpectedly')
|
|
257
269
|
} catch (err) {
|
|
258
270
|
if (!closed) onError?.(err)
|
|
259
271
|
}
|
package/src/index.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import assert from 'node:assert/strict'
|
|
2
|
-
import { describe, test } from 'node:test'
|
|
2
|
+
import { describe, test, afterEach } from 'node:test'
|
|
3
3
|
|
|
4
4
|
import { CorePikkuFetch, corePikkuFetch } from './index.js'
|
|
5
5
|
|
|
@@ -9,3 +9,46 @@ describe('@pikku/fetch', () => {
|
|
|
9
9
|
assert.equal(typeof corePikkuFetch, 'function')
|
|
10
10
|
})
|
|
11
11
|
})
|
|
12
|
+
|
|
13
|
+
describe('CorePikkuFetch.setHeader', () => {
|
|
14
|
+
const realFetch = globalThis.fetch
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
globalThis.fetch = realFetch
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const capture = () => {
|
|
20
|
+
const seen: { headers?: Record<string, string> } = {}
|
|
21
|
+
globalThis.fetch = (async (_input: any, init?: any) => {
|
|
22
|
+
seen.headers = (init?.headers ?? {}) as Record<string, string>
|
|
23
|
+
return new Response('{}', {
|
|
24
|
+
status: 200,
|
|
25
|
+
headers: { 'content-type': 'application/json' },
|
|
26
|
+
})
|
|
27
|
+
}) as typeof fetch
|
|
28
|
+
return seen
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
test('sends a set header on every request', async () => {
|
|
32
|
+
const seen = capture()
|
|
33
|
+
const client = new CorePikkuFetch({ serverUrl: 'https://api.test' })
|
|
34
|
+
client.setHeader('x-pikku-impersonate-user-id', 'u_guest')
|
|
35
|
+
await client.fetch('/anything', 'GET', undefined)
|
|
36
|
+
assert.equal(seen.headers?.['x-pikku-impersonate-user-id'], 'u_guest')
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test('removes the header when set to null', async () => {
|
|
40
|
+
const seen = capture()
|
|
41
|
+
const client = new CorePikkuFetch({ serverUrl: 'https://api.test' })
|
|
42
|
+
client.setHeader('x-pikku-impersonate-user-id', 'u_guest')
|
|
43
|
+
client.setHeader('x-pikku-impersonate-user-id', null)
|
|
44
|
+
await client.fetch('/anything', 'GET', undefined)
|
|
45
|
+
assert.equal(seen.headers?.['x-pikku-impersonate-user-id'], undefined)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('no header is sent by default (inherits the session)', async () => {
|
|
49
|
+
const seen = capture()
|
|
50
|
+
const client = new CorePikkuFetch({ serverUrl: 'https://api.test' })
|
|
51
|
+
await client.fetch('/anything', 'GET', undefined)
|
|
52
|
+
assert.equal(seen.headers?.['x-pikku-impersonate-user-id'], undefined)
|
|
53
|
+
})
|
|
54
|
+
})
|