@outputai/http 0.4.1-dev.7b85c96.0 → 0.4.1-dev.ae4bd16.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.d.ts +2 -9
- package/dist/cost.js +5 -4
- package/dist/cost.spec.js +42 -48
- package/dist/fetch/index.d.ts +1 -1
- package/dist/fetch/index.js +3 -3
- package/dist/fetch/logger.d.ts +2 -2
- package/dist/fetch/logger.js +5 -5
- package/dist/fetch/logger.spec.js +37 -19
- package/package.json +2 -2
package/dist/cost.d.ts
CHANGED
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
import { KyResponse } from 'ky';
|
|
2
|
-
export type RequestCost = {
|
|
3
|
-
total: number;
|
|
4
|
-
components?: Array<{
|
|
5
|
-
name: string;
|
|
6
|
-
value: number;
|
|
7
|
-
}>;
|
|
8
|
-
};
|
|
9
2
|
/**
|
|
10
3
|
* Attach cost information to the trace of an HTTP Request using the response
|
|
11
4
|
*
|
|
12
5
|
* @param response - The response of the HTTP Request to attach the information
|
|
13
|
-
* @param
|
|
6
|
+
* @param value - The price of the HTTP request
|
|
14
7
|
* @returns
|
|
15
8
|
*/
|
|
16
|
-
export declare const addRequestCost: (response: KyResponse | Response,
|
|
9
|
+
export declare const addRequestCost: (response: KyResponse | Response, value: number) => void;
|
package/dist/cost.js
CHANGED
|
@@ -4,15 +4,16 @@ import { requestIdSymbol } from './consts.js';
|
|
|
4
4
|
* Attach cost information to the trace of an HTTP Request using the response
|
|
5
5
|
*
|
|
6
6
|
* @param response - The response of the HTTP Request to attach the information
|
|
7
|
-
* @param
|
|
7
|
+
* @param value - The price of the HTTP request
|
|
8
8
|
* @returns
|
|
9
9
|
*/
|
|
10
|
-
export const addRequestCost = (response,
|
|
10
|
+
export const addRequestCost = (response, value) => {
|
|
11
11
|
const eventId = Reflect.get(response, requestIdSymbol);
|
|
12
12
|
if (!eventId) {
|
|
13
13
|
console.warn('addRequestCost(): The "response" argument did not originate from @outputai/http, no costs were added.');
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
const attribute = new Tracing.Attribute.HTTPRequestCost(response.url, eventId, value);
|
|
17
|
+
Tracing.addEventAttribute({ eventId, attribute });
|
|
18
|
+
emitEvent('cost:http:request', attribute);
|
|
18
19
|
};
|
package/dist/cost.spec.js
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
2
|
import { requestIdSymbol } from './consts.js';
|
|
3
|
-
vi.mock('@outputai/core/sdk_activity_integration', () =>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
vi.mock('@outputai/core/sdk_activity_integration', () => {
|
|
4
|
+
class HTTPRequestCost {
|
|
5
|
+
static TYPE = 'http:request:cost';
|
|
6
|
+
type = HTTPRequestCost.TYPE;
|
|
7
|
+
url;
|
|
8
|
+
requestId;
|
|
9
|
+
total;
|
|
10
|
+
constructor(url, requestId, total) {
|
|
11
|
+
this.url = url;
|
|
12
|
+
this.requestId = requestId;
|
|
13
|
+
this.total = total;
|
|
8
14
|
}
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
Tracing: {
|
|
18
|
+
addEventAttribute: vi.fn(),
|
|
19
|
+
Attribute: {
|
|
20
|
+
HTTPRequestCost
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
emitEvent: vi.fn()
|
|
24
|
+
};
|
|
25
|
+
});
|
|
12
26
|
import { Tracing, emitEvent } from '@outputai/core/sdk_activity_integration';
|
|
13
27
|
import { addRequestCost } from './cost.js';
|
|
14
28
|
const tracing = vi.mocked(Tracing, true);
|
|
@@ -24,7 +38,7 @@ describe('addRequestCost', () => {
|
|
|
24
38
|
});
|
|
25
39
|
it('shortcircuits when the response has no http request id', () => {
|
|
26
40
|
const response = new Response();
|
|
27
|
-
const cost =
|
|
41
|
+
const cost = 1;
|
|
28
42
|
addRequestCost(response, cost);
|
|
29
43
|
expect(console.warn).toHaveBeenCalledWith('addRequestCost(): The "response" argument did not originate from @outputai/http, no costs were added.');
|
|
30
44
|
expect(tracing.addEventAttribute).not.toHaveBeenCalled();
|
|
@@ -33,56 +47,36 @@ describe('addRequestCost', () => {
|
|
|
33
47
|
it('records cost on the trace event when the response carries the request id', () => {
|
|
34
48
|
const response = new Response(undefined, { status: 200 });
|
|
35
49
|
Reflect.set(response, requestIdSymbol, 'evt-cost-1');
|
|
36
|
-
const cost =
|
|
50
|
+
const cost = 2.5;
|
|
37
51
|
addRequestCost(response, cost);
|
|
38
52
|
expect(console.warn).not.toHaveBeenCalled();
|
|
39
53
|
expect(tracing.addEventAttribute).toHaveBeenCalledWith({
|
|
40
54
|
eventId: 'evt-cost-1',
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
cost
|
|
55
|
+
attribute: expect.objectContaining({
|
|
56
|
+
type: Tracing.Attribute.HTTPRequestCost.TYPE,
|
|
57
|
+
url: response.url,
|
|
58
|
+
requestId: 'evt-cost-1',
|
|
59
|
+
total: cost
|
|
60
|
+
})
|
|
48
61
|
});
|
|
62
|
+
const attribute = tracing.addEventAttribute.mock.calls[0][0].attribute;
|
|
63
|
+
expect(emit).toHaveBeenCalledWith('cost:http:request', attribute);
|
|
49
64
|
});
|
|
50
|
-
it('
|
|
65
|
+
it('records zero cost on the trace event', () => {
|
|
51
66
|
const response = new Response();
|
|
52
67
|
Reflect.set(response, requestIdSymbol, 'evt-cost-2');
|
|
53
|
-
const cost =
|
|
54
|
-
total: 10,
|
|
55
|
-
components: [
|
|
56
|
-
{ name: 'input', value: 3 },
|
|
57
|
-
{ name: 'output', value: 7 }
|
|
58
|
-
]
|
|
59
|
-
};
|
|
68
|
+
const cost = 0;
|
|
60
69
|
addRequestCost(response, cost);
|
|
61
70
|
expect(tracing.addEventAttribute).toHaveBeenCalledWith({
|
|
62
71
|
eventId: 'evt-cost-2',
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
cost
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
it('forwards an empty components array to tracing', () => {
|
|
73
|
-
const response = new Response();
|
|
74
|
-
Reflect.set(response, requestIdSymbol, 'evt-cost-3');
|
|
75
|
-
const cost = { total: 1, components: [] };
|
|
76
|
-
addRequestCost(response, cost);
|
|
77
|
-
expect(tracing.addEventAttribute).toHaveBeenCalledWith({
|
|
78
|
-
eventId: 'evt-cost-3',
|
|
79
|
-
name: Tracing.Attribute.COST,
|
|
80
|
-
value: cost
|
|
81
|
-
});
|
|
82
|
-
expect(emit).toHaveBeenCalledWith('cost:http:request', {
|
|
83
|
-
requestId: 'evt-cost-3',
|
|
84
|
-
url: response.url,
|
|
85
|
-
cost
|
|
72
|
+
attribute: expect.objectContaining({
|
|
73
|
+
type: Tracing.Attribute.HTTPRequestCost.TYPE,
|
|
74
|
+
url: response.url,
|
|
75
|
+
requestId: 'evt-cost-2',
|
|
76
|
+
total: cost
|
|
77
|
+
})
|
|
86
78
|
});
|
|
79
|
+
const attribute = tracing.addEventAttribute.mock.calls[0][0].attribute;
|
|
80
|
+
expect(emit).toHaveBeenCalledWith('cost:http:request', attribute);
|
|
87
81
|
});
|
|
88
82
|
});
|
package/dist/fetch/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ RequestInit };
|
|
|
13
13
|
* Behaves the same as any fetch function except:
|
|
14
14
|
* - Sets a request header called `x-request--trace-id` with a random UUID;
|
|
15
15
|
* - Sends the request, response, error and/or failure to the Trace system;
|
|
16
|
-
* - Emits a `http:request` event on every call (success,
|
|
16
|
+
* - Emits a `http:request` event on every call (success, error, failure).
|
|
17
17
|
*
|
|
18
18
|
* @see {@link https://fetch.spec.whatwg.org/}
|
|
19
19
|
* @param input - URL string, URL object or Request object (undici's or Node's)
|
package/dist/fetch/index.js
CHANGED
|
@@ -15,7 +15,7 @@ export * as undici from 'undici';
|
|
|
15
15
|
* Behaves the same as any fetch function except:
|
|
16
16
|
* - Sets a request header called `x-request--trace-id` with a random UUID;
|
|
17
17
|
* - Sends the request, response, error and/or failure to the Trace system;
|
|
18
|
-
* - Emits a `http:request` event on every call (success,
|
|
18
|
+
* - Emits a `http:request` event on every call (success, error, failure).
|
|
19
19
|
*
|
|
20
20
|
* @see {@link https://fetch.spec.whatwg.org/}
|
|
21
21
|
* @param input - URL string, URL object or Request object (undici's or Node's)
|
|
@@ -36,7 +36,7 @@ export const fetch = async (input, init) => {
|
|
|
36
36
|
await logRequest({ requestId, request });
|
|
37
37
|
try {
|
|
38
38
|
const response = await undici.fetch(request);
|
|
39
|
-
const durationMs =
|
|
39
|
+
const durationMs = performance.now() - startedAt;
|
|
40
40
|
// This enriches the response of the request id, so it is identifiable later.
|
|
41
41
|
addRequestIdToResponse(response, requestId);
|
|
42
42
|
if (response.status > 399) {
|
|
@@ -47,7 +47,7 @@ export const fetch = async (input, init) => {
|
|
|
47
47
|
return response;
|
|
48
48
|
}
|
|
49
49
|
catch (error) {
|
|
50
|
-
const durationMs =
|
|
50
|
+
const durationMs = performance.now() - startedAt;
|
|
51
51
|
logFailure({ requestId, error: error, method, url, durationMs });
|
|
52
52
|
throw error;
|
|
53
53
|
}
|
package/dist/fetch/logger.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export declare const logRequest: ({ requestId, request }: {
|
|
|
12
12
|
}) => Promise<void>;
|
|
13
13
|
/**
|
|
14
14
|
* Sends the trace error event for an http response with error status
|
|
15
|
-
* and emits a `http:request` event with `outcome: '
|
|
15
|
+
* and emits a `http:request` event with `outcome: 'error'`.
|
|
16
16
|
*
|
|
17
17
|
* @param options
|
|
18
18
|
* @param options.requestId - id of the request
|
|
@@ -48,7 +48,7 @@ export declare const logResponse: ({ requestId, response, method, url, durationM
|
|
|
48
48
|
}) => Promise<void>;
|
|
49
49
|
/**
|
|
50
50
|
* Creates the trace error event for a network/connection failure
|
|
51
|
-
* and emits a `http:request` event with `outcome: '
|
|
51
|
+
* and emits a `http:request` event with `outcome: 'failure'`.
|
|
52
52
|
*
|
|
53
53
|
* @param options
|
|
54
54
|
* @param options.requestId - id of the request
|
package/dist/fetch/logger.js
CHANGED
|
@@ -20,11 +20,11 @@ export const logRequest = async ({ requestId, request }) => {
|
|
|
20
20
|
...(config.logVerbose && { headers: redactHeaders(request.headers), body: await parseBody(request) })
|
|
21
21
|
}
|
|
22
22
|
});
|
|
23
|
-
Tracing.addEventAttribute({ eventId: requestId,
|
|
23
|
+
Tracing.addEventAttribute({ eventId: requestId, attribute: new Tracing.Attribute.HTTPRequestCount(request.url, requestId) });
|
|
24
24
|
};
|
|
25
25
|
/**
|
|
26
26
|
* Sends the trace error event for an http response with error status
|
|
27
|
-
* and emits a `http:request` event with `outcome: '
|
|
27
|
+
* and emits a `http:request` event with `outcome: 'error'`.
|
|
28
28
|
*
|
|
29
29
|
* @param options
|
|
30
30
|
* @param options.requestId - id of the request
|
|
@@ -43,7 +43,7 @@ export const logError = async ({ requestId, response, method, url, durationMs })
|
|
|
43
43
|
}
|
|
44
44
|
});
|
|
45
45
|
emitHttpRequestEvent({
|
|
46
|
-
requestId, method, url, status: response.status, durationMs, outcome: '
|
|
46
|
+
requestId, method, url, status: response.status, durationMs, outcome: 'error'
|
|
47
47
|
});
|
|
48
48
|
};
|
|
49
49
|
/**
|
|
@@ -71,7 +71,7 @@ export const logResponse = async ({ requestId, response, method, url, durationMs
|
|
|
71
71
|
};
|
|
72
72
|
/**
|
|
73
73
|
* Creates the trace error event for a network/connection failure
|
|
74
|
-
* and emits a `http:request` event with `outcome: '
|
|
74
|
+
* and emits a `http:request` event with `outcome: 'failure'`.
|
|
75
75
|
*
|
|
76
76
|
* @param options
|
|
77
77
|
* @param options.requestId - id of the request
|
|
@@ -83,6 +83,6 @@ export const logResponse = async ({ requestId, response, method, url, durationMs
|
|
|
83
83
|
export const logFailure = ({ requestId, error, method, url, durationMs }) => {
|
|
84
84
|
Tracing.addEventError({ id: requestId, details: serializeError(error) });
|
|
85
85
|
emitHttpRequestEvent({
|
|
86
|
-
requestId, method, url, status: undefined, durationMs, outcome: '
|
|
86
|
+
requestId, method, url, status: undefined, durationMs, outcome: 'failure'
|
|
87
87
|
});
|
|
88
88
|
};
|
|
@@ -1,14 +1,29 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
2
|
import { Response, Request } from 'undici';
|
|
3
|
-
vi.mock('@outputai/core/sdk_activity_integration', () =>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
vi.mock('@outputai/core/sdk_activity_integration', () => {
|
|
4
|
+
class HTTPRequestCount {
|
|
5
|
+
static TYPE = 'http:request:count';
|
|
6
|
+
type = HTTPRequestCount.TYPE;
|
|
7
|
+
url;
|
|
8
|
+
requestId;
|
|
9
|
+
constructor(url, requestId) {
|
|
10
|
+
this.url = url;
|
|
11
|
+
this.requestId = requestId;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
Tracing: {
|
|
16
|
+
addEventStart: vi.fn(),
|
|
17
|
+
addEventEnd: vi.fn(),
|
|
18
|
+
addEventError: vi.fn(),
|
|
19
|
+
addEventAttribute: vi.fn(),
|
|
20
|
+
Attribute: {
|
|
21
|
+
HTTPRequestCount
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
emitEvent: vi.fn()
|
|
25
|
+
};
|
|
26
|
+
});
|
|
12
27
|
import { Tracing, emitEvent } from '@outputai/core/sdk_activity_integration';
|
|
13
28
|
const tracing = vi.mocked(Tracing, true);
|
|
14
29
|
const emit = vi.mocked(emitEvent, true);
|
|
@@ -32,11 +47,14 @@ beforeEach(() => {
|
|
|
32
47
|
});
|
|
33
48
|
describe('fetch/logger', () => {
|
|
34
49
|
describe('logRequest', () => {
|
|
35
|
-
const
|
|
50
|
+
const expectRequestCountAttribute = (requestId, url) => {
|
|
36
51
|
expect(tracing.addEventAttribute).toHaveBeenCalledWith({
|
|
37
52
|
eventId: requestId,
|
|
38
|
-
|
|
39
|
-
|
|
53
|
+
attribute: expect.objectContaining({
|
|
54
|
+
type: Tracing.Attribute.HTTPRequestCount.TYPE,
|
|
55
|
+
url,
|
|
56
|
+
requestId
|
|
57
|
+
})
|
|
40
58
|
});
|
|
41
59
|
};
|
|
42
60
|
it('records minimal details when verbose is off', async () => {
|
|
@@ -52,14 +70,14 @@ describe('fetch/logger', () => {
|
|
|
52
70
|
url: 'https://api.example.com/r'
|
|
53
71
|
}
|
|
54
72
|
});
|
|
55
|
-
|
|
73
|
+
expectRequestCountAttribute('req-1', 'https://api.example.com/r');
|
|
56
74
|
});
|
|
57
75
|
it('defaults method to GET', async () => {
|
|
58
76
|
const { logRequest } = await logLogger(false);
|
|
59
77
|
const request = new Request('https://x.test');
|
|
60
78
|
await logRequest({ requestId: 'r2', request });
|
|
61
79
|
expect(tracing.addEventStart.mock.calls[0][0].details.method).toBe('GET');
|
|
62
|
-
|
|
80
|
+
expectRequestCountAttribute('r2', 'https://x.test/');
|
|
63
81
|
});
|
|
64
82
|
it('includes redacted headers and parsed body when verbose is on', async () => {
|
|
65
83
|
const { logRequest } = await logLogger(true);
|
|
@@ -84,7 +102,7 @@ describe('fetch/logger', () => {
|
|
|
84
102
|
body: { x: 1 }
|
|
85
103
|
}
|
|
86
104
|
});
|
|
87
|
-
|
|
105
|
+
expectRequestCountAttribute('req-v', 'https://api.example.com/p');
|
|
88
106
|
});
|
|
89
107
|
});
|
|
90
108
|
describe('logError', () => {
|
|
@@ -222,7 +240,7 @@ describe('fetch/logger', () => {
|
|
|
222
240
|
outcome: 'success'
|
|
223
241
|
});
|
|
224
242
|
});
|
|
225
|
-
it('emits http:request with outcome=
|
|
243
|
+
it('emits http:request with outcome=error on logError', async () => {
|
|
226
244
|
const { logError } = await logLogger(false);
|
|
227
245
|
const response = new Response('boom', { status: 500 });
|
|
228
246
|
await logError({
|
|
@@ -238,10 +256,10 @@ describe('fetch/logger', () => {
|
|
|
238
256
|
url: 'https://api.example.com/err',
|
|
239
257
|
status: 500,
|
|
240
258
|
durationMs: 15,
|
|
241
|
-
outcome: '
|
|
259
|
+
outcome: 'error'
|
|
242
260
|
});
|
|
243
261
|
});
|
|
244
|
-
it('emits http:request with outcome=
|
|
262
|
+
it('emits http:request with outcome=failure on logFailure (status undefined)', async () => {
|
|
245
263
|
const { logFailure } = await logLogger(false);
|
|
246
264
|
const err = new TypeError('network');
|
|
247
265
|
logFailure({
|
|
@@ -257,7 +275,7 @@ describe('fetch/logger', () => {
|
|
|
257
275
|
url: 'https://api.example.com/net',
|
|
258
276
|
status: undefined,
|
|
259
277
|
durationMs: 9,
|
|
260
|
-
outcome: '
|
|
278
|
+
outcome: 'failure'
|
|
261
279
|
});
|
|
262
280
|
});
|
|
263
281
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@outputai/http",
|
|
3
|
-
"version": "0.4.1-dev.
|
|
3
|
+
"version": "0.4.1-dev.ae4bd16.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.4.1-dev.
|
|
14
|
+
"@outputai/core": "0.4.1-dev.ae4bd16.0"
|
|
15
15
|
},
|
|
16
16
|
"license": "Apache-2.0",
|
|
17
17
|
"publishConfig": {
|