@outputai/http 0.5.2-next.93dd22e.0 → 0.5.2-next.b54869d.0
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/dist/cost.spec.js +23 -0
- package/dist/fetch/utils.d.ts +7 -7
- package/dist/fetch/utils.js +20 -7
- package/dist/fetch/utils.spec.js +11 -3
- package/package.json +2 -2
package/dist/cost.spec.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { Response } from 'undici';
|
|
2
3
|
import { requestIdSymbol } from './consts.js';
|
|
3
4
|
vi.mock('@outputai/core/sdk_activity_integration', () => {
|
|
4
5
|
class HTTPRequestCost {
|
|
@@ -25,6 +26,7 @@ vi.mock('@outputai/core/sdk_activity_integration', () => {
|
|
|
25
26
|
});
|
|
26
27
|
import { Tracing, emitEvent } from '@outputai/core/sdk_activity_integration';
|
|
27
28
|
import { addRequestCost } from './cost.js';
|
|
29
|
+
import { addRequestIdToResponse } from './fetch/utils.js';
|
|
28
30
|
const tracing = vi.mocked(Tracing, true);
|
|
29
31
|
const emit = vi.mocked(emitEvent, true);
|
|
30
32
|
describe('addRequestCost', () => {
|
|
@@ -79,4 +81,25 @@ describe('addRequestCost', () => {
|
|
|
79
81
|
const attribute = tracing.addEventAttribute.mock.calls[0][0].attribute;
|
|
80
82
|
expect(emit).toHaveBeenCalledWith('cost:http:request', attribute);
|
|
81
83
|
});
|
|
84
|
+
// ky clones the response before passing it to afterResponse hooks. Without
|
|
85
|
+
// the clone-propagation patch in addRequestIdToResponse, this path silently
|
|
86
|
+
// dropped cost (warned "did not originate from @outputai/http") for every
|
|
87
|
+
// service client that emits cost from inside a ky hook.
|
|
88
|
+
it('records cost on a cloned response (regression: ky afterResponse hooks)', () => {
|
|
89
|
+
const response = new Response(undefined, { status: 200 });
|
|
90
|
+
addRequestIdToResponse(response, 'evt-clone-1');
|
|
91
|
+
const cloned = response.clone();
|
|
92
|
+
addRequestCost(cloned, 4.2);
|
|
93
|
+
expect(console.warn).not.toHaveBeenCalled();
|
|
94
|
+
expect(tracing.addEventAttribute).toHaveBeenCalledWith({
|
|
95
|
+
eventId: 'evt-clone-1',
|
|
96
|
+
attribute: expect.objectContaining({
|
|
97
|
+
type: Tracing.Attribute.HTTPRequestCost.TYPE,
|
|
98
|
+
requestId: 'evt-clone-1',
|
|
99
|
+
total: 4.2
|
|
100
|
+
})
|
|
101
|
+
});
|
|
102
|
+
const attribute = tracing.addEventAttribute.mock.calls[0][0].attribute;
|
|
103
|
+
expect(emit).toHaveBeenCalledWith('cost:http:request', attribute);
|
|
104
|
+
});
|
|
82
105
|
});
|
package/dist/fetch/utils.d.ts
CHANGED
|
@@ -35,11 +35,11 @@ export declare const redactHeaders: (headers: Headers) => Record<string, unknown
|
|
|
35
35
|
*/
|
|
36
36
|
export declare const parseBody: (r: Request | Response) => Promise<string | object>;
|
|
37
37
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
38
|
+
* Tag a response in place with its request id so downstream code (e.g.
|
|
39
|
+
* `addRequestCost`) can correlate. Stores the id under a private symbol AND
|
|
40
|
+
* patches `clone()` so the tag propagates to clones — ky clones the response
|
|
41
|
+
* before invoking `afterResponse` hooks, and undici headers are immutable on
|
|
42
|
+
* received responses, so a symbol re-attached inside `clone()` is the only
|
|
43
|
+
* path that survives.
|
|
44
44
|
*/
|
|
45
|
-
export declare const addRequestIdToResponse: (response: Response, requestId: string) =>
|
|
45
|
+
export declare const addRequestIdToResponse: (response: Response, requestId: string) => void;
|
package/dist/fetch/utils.js
CHANGED
|
@@ -91,11 +91,24 @@ export const parseBody = async (r) => {
|
|
|
91
91
|
}
|
|
92
92
|
};
|
|
93
93
|
/**
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
94
|
+
* Tag a response in place with its request id so downstream code (e.g.
|
|
95
|
+
* `addRequestCost`) can correlate. Stores the id under a private symbol AND
|
|
96
|
+
* patches `clone()` so the tag propagates to clones — ky clones the response
|
|
97
|
+
* before invoking `afterResponse` hooks, and undici headers are immutable on
|
|
98
|
+
* received responses, so a symbol re-attached inside `clone()` is the only
|
|
99
|
+
* path that survives.
|
|
100
100
|
*/
|
|
101
|
-
export const addRequestIdToResponse = (response, requestId) =>
|
|
101
|
+
export const addRequestIdToResponse = (response, requestId) => {
|
|
102
|
+
Object.defineProperty(response, requestIdSymbol, { value: requestId, enumerable: false, configurable: false, writable: false });
|
|
103
|
+
const originalClone = response.clone.bind(response);
|
|
104
|
+
Object.defineProperty(response, 'clone', {
|
|
105
|
+
value: function clone() {
|
|
106
|
+
const cloned = originalClone();
|
|
107
|
+
addRequestIdToResponse(cloned, requestId);
|
|
108
|
+
return cloned;
|
|
109
|
+
},
|
|
110
|
+
enumerable: false,
|
|
111
|
+
configurable: true,
|
|
112
|
+
writable: true
|
|
113
|
+
});
|
|
114
|
+
};
|
package/dist/fetch/utils.spec.js
CHANGED
|
@@ -252,10 +252,9 @@ describe('fetch/utils', () => {
|
|
|
252
252
|
});
|
|
253
253
|
});
|
|
254
254
|
describe('addRequestIdToResponse', () => {
|
|
255
|
-
it('stores request id under requestIdSymbol
|
|
255
|
+
it('stores request id under requestIdSymbol on the response', () => {
|
|
256
256
|
const response = new Response('ok');
|
|
257
|
-
|
|
258
|
-
expect(enriched).toBe(response);
|
|
257
|
+
addRequestIdToResponse(response, 'req-123');
|
|
259
258
|
expect(response[requestIdSymbol]).toBe('req-123');
|
|
260
259
|
});
|
|
261
260
|
it('defines request id as non-enumerable, non-writable and non-configurable', () => {
|
|
@@ -270,5 +269,14 @@ describe('fetch/utils', () => {
|
|
|
270
269
|
value: 'req-456'
|
|
271
270
|
});
|
|
272
271
|
});
|
|
272
|
+
it('propagates the request id to clones (ky afterResponse passes the clone)', () => {
|
|
273
|
+
const response = new Response('ok');
|
|
274
|
+
addRequestIdToResponse(response, 'req-789');
|
|
275
|
+
const cloned = response.clone();
|
|
276
|
+
expect(cloned[requestIdSymbol]).toBe('req-789');
|
|
277
|
+
// recursion: clones of clones inherit too
|
|
278
|
+
const grandchild = cloned.clone();
|
|
279
|
+
expect(grandchild[requestIdSymbol]).toBe('req-789');
|
|
280
|
+
});
|
|
273
281
|
});
|
|
274
282
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@outputai/http",
|
|
3
|
-
"version": "0.5.2-next.
|
|
3
|
+
"version": "0.5.2-next.b54869d.0",
|
|
4
4
|
"description": "Framework abstraction to make HTTP calls with tracing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"ky": "1.14.3",
|
|
13
13
|
"undici": "8.1.0",
|
|
14
|
-
"@outputai/core": "0.5.2-next.
|
|
14
|
+
"@outputai/core": "0.5.2-next.b54869d.0"
|
|
15
15
|
},
|
|
16
16
|
"license": "Apache-2.0",
|
|
17
17
|
"publishConfig": {
|