@servicetitan/journey 2.0.0 → 3.0.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/README.md +1 -1
- package/dist/__test-utils__/test-runtime.d.ts.map +1 -0
- package/dist/__test-utils__/test-runtime.js +54 -0
- package/dist/__test-utils__/test-runtime.js.map +1 -0
- package/dist/__tests__/core/ambient.test.d.ts.map +1 -0
- package/dist/__tests__/core/api.test.d.ts +2 -0
- package/dist/__tests__/core/api.test.d.ts.map +1 -0
- package/dist/__tests__/core/define-journey.test.d.ts.map +1 -0
- package/dist/__tests__/core/requests.test.d.ts.map +1 -0
- package/dist/__tests__/core/runtime.test.d.ts.map +1 -0
- package/dist/__tests__/core/sanitize.test.d.ts +2 -0
- package/dist/__tests__/core/sanitize.test.d.ts.map +1 -0
- package/dist/__tests__/core/step.test.d.ts.map +1 -0
- package/dist/__tests__/core/timeouts.test.d.ts.map +1 -0
- package/dist/__tests__/global.test.d.ts +3 -0
- package/dist/__tests__/global.test.d.ts.map +1 -0
- package/dist/__tests__/hardening.test.d.ts +2 -0
- package/dist/__tests__/hardening.test.d.ts.map +1 -0
- package/dist/__tests__/integrations/axios.test.d.ts.map +1 -0
- package/dist/__tests__/integrations/context.test.d.ts.map +1 -0
- package/dist/__tests__/integrations/expose-app-journey.test.d.ts.map +1 -0
- package/dist/__tests__/integrations/fetch.test.d.ts.map +1 -0
- package/dist/__tests__/integrations/host-mfe.test.d.ts +2 -0
- package/dist/__tests__/integrations/host-mfe.test.d.ts.map +1 -0
- package/dist/__tests__/integrations/jquery.test.d.ts.map +1 -0
- package/dist/__tests__/integrations/request-key.test.d.ts.map +1 -0
- package/dist/__tests__/integrations/scope.test.d.ts.map +1 -0
- package/dist/__tests__/integrations/use-journey-step.test.d.ts +2 -0
- package/dist/__tests__/integrations/use-journey-step.test.d.ts.map +1 -0
- package/dist/__tests__/sinks/console.test.d.ts +2 -0
- package/dist/__tests__/sinks/console.test.d.ts.map +1 -0
- package/dist/__tests__/sinks/datadog.test.d.ts.map +1 -0
- package/dist/__tests__/sinks/sinks.test.d.ts.map +1 -0
- package/dist/config.d.ts +61 -13
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +98 -29
- package/dist/config.js.map +1 -1
- package/dist/core/abort-error.d.ts +4 -0
- package/dist/core/abort-error.d.ts.map +1 -0
- package/dist/core/abort-error.js +22 -0
- package/dist/core/abort-error.js.map +1 -0
- package/dist/core/api.d.ts +43 -0
- package/dist/core/api.d.ts.map +1 -0
- package/dist/core/api.js +122 -0
- package/dist/core/api.js.map +1 -0
- package/dist/{endpoint-policy.d.ts → core/endpoint-policy.d.ts} +14 -4
- package/dist/core/endpoint-policy.d.ts.map +1 -0
- package/dist/core/endpoint-policy.js +70 -0
- package/dist/core/endpoint-policy.js.map +1 -0
- package/dist/core/index.d.ts +6 -5
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +4 -3
- package/dist/core/index.js.map +1 -1
- package/dist/core/limits.d.ts +29 -0
- package/dist/core/limits.d.ts.map +1 -0
- package/dist/core/limits.js +63 -0
- package/dist/core/limits.js.map +1 -0
- package/dist/core/request-policy.d.ts +39 -0
- package/dist/core/request-policy.d.ts.map +1 -0
- package/dist/core/request-policy.js +18 -0
- package/dist/core/request-policy.js.map +1 -0
- package/dist/core/runtime.d.ts +95 -17
- package/dist/core/runtime.d.ts.map +1 -1
- package/dist/core/runtime.js +435 -124
- package/dist/core/runtime.js.map +1 -1
- package/dist/core/sanitize.d.ts +23 -0
- package/dist/core/sanitize.d.ts.map +1 -0
- package/dist/core/sanitize.js +143 -0
- package/dist/core/sanitize.js.map +1 -0
- package/dist/core/step-tag.d.ts +31 -6
- package/dist/core/step-tag.d.ts.map +1 -1
- package/dist/core/step-tag.js +62 -9
- package/dist/core/step-tag.js.map +1 -1
- package/dist/core/types.d.ts +76 -26
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +4 -1
- package/dist/core/types.js.map +1 -1
- package/dist/global.d.ts +145 -0
- package/dist/global.d.ts.map +1 -0
- package/dist/global.js +326 -0
- package/dist/global.js.map +1 -0
- package/dist/index.d.ts +9 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -5
- package/dist/index.js.map +1 -1
- package/dist/integrations/axios.d.ts +5 -1
- package/dist/integrations/axios.d.ts.map +1 -1
- package/dist/integrations/axios.js +146 -34
- package/dist/integrations/axios.js.map +1 -1
- package/dist/integrations/fetch.d.ts +6 -4
- package/dist/integrations/fetch.d.ts.map +1 -1
- package/dist/integrations/fetch.js +105 -26
- package/dist/integrations/fetch.js.map +1 -1
- package/dist/integrations/http-report.d.ts +27 -0
- package/dist/integrations/http-report.d.ts.map +1 -0
- package/dist/integrations/http-report.js +123 -0
- package/dist/integrations/http-report.js.map +1 -0
- package/dist/integrations/jquery/index.d.ts +10 -6
- package/dist/integrations/jquery/index.d.ts.map +1 -1
- package/dist/integrations/jquery/index.js +28 -9
- package/dist/integrations/jquery/index.js.map +1 -1
- package/dist/integrations/jquery/instrument.d.ts +14 -5
- package/dist/integrations/jquery/instrument.d.ts.map +1 -1
- package/dist/integrations/jquery/instrument.js +111 -29
- package/dist/integrations/jquery/instrument.js.map +1 -1
- package/dist/integrations/react/context.d.ts +4 -1
- package/dist/integrations/react/context.d.ts.map +1 -1
- package/dist/integrations/react/context.js +4 -1
- package/dist/integrations/react/context.js.map +1 -1
- package/dist/integrations/react/scope.d.ts +14 -3
- package/dist/integrations/react/scope.d.ts.map +1 -1
- package/dist/integrations/react/scope.js +48 -9
- package/dist/integrations/react/scope.js.map +1 -1
- package/dist/integrations/react/stable-config-keys.d.ts +6 -0
- package/dist/integrations/react/stable-config-keys.d.ts.map +1 -0
- package/dist/integrations/react/stable-config-keys.js +21 -0
- package/dist/integrations/react/stable-config-keys.js.map +1 -0
- package/dist/integrations/react/use-journey-step.d.ts +11 -0
- package/dist/integrations/react/use-journey-step.d.ts.map +1 -0
- package/dist/integrations/react/use-journey-step.js +19 -0
- package/dist/integrations/react/use-journey-step.js.map +1 -0
- package/dist/integrations/request-key.d.ts +8 -3
- package/dist/integrations/request-key.d.ts.map +1 -1
- package/dist/integrations/request-key.js +10 -9
- package/dist/integrations/request-key.js.map +1 -1
- package/dist/integrations/soft-skip.d.ts +7 -0
- package/dist/integrations/soft-skip.d.ts.map +1 -0
- package/dist/integrations/soft-skip.js +30 -0
- package/dist/integrations/soft-skip.js.map +1 -0
- package/dist/sinks/console.d.ts +11 -0
- package/dist/sinks/console.d.ts.map +1 -0
- package/dist/sinks/console.js +19 -0
- package/dist/sinks/console.js.map +1 -0
- package/dist/sinks/datadog.d.ts +12 -1
- package/dist/sinks/datadog.d.ts.map +1 -1
- package/dist/sinks/datadog.js +42 -25
- package/dist/sinks/datadog.js.map +1 -1
- package/package.json +2 -2
- package/src/{__mocks__ → __test-utils__}/test-runtime.ts +15 -4
- package/src/__tests__/config.test.ts +158 -42
- package/src/__tests__/{ambient.test.ts → core/ambient.test.ts} +23 -16
- package/src/__tests__/{registry.test.ts → core/api.test.ts} +35 -30
- package/src/__tests__/{define-journey.test.ts → core/define-journey.test.ts} +33 -15
- package/src/__tests__/{requests.test.ts → core/requests.test.ts} +61 -39
- package/src/__tests__/{runtime.test.ts → core/runtime.test.ts} +84 -37
- package/src/__tests__/core/sanitize.test.ts +69 -0
- package/src/__tests__/{step.test.ts → core/step.test.ts} +146 -52
- package/src/__tests__/{timeouts.test.ts → core/timeouts.test.ts} +83 -13
- package/src/__tests__/global.test.ts +154 -0
- package/src/__tests__/hardening.test.ts +613 -0
- package/src/__tests__/integrations/axios.test.ts +616 -0
- package/src/__tests__/{context.test.tsx → integrations/context.test.tsx} +1 -1
- package/src/__tests__/{expose-app-journey.test.ts → integrations/expose-app-journey.test.ts} +11 -5
- package/src/__tests__/{fetch.test.ts → integrations/fetch.test.ts} +159 -25
- package/src/__tests__/integrations/host-mfe.test.ts +548 -0
- package/src/__tests__/integrations/jquery.test.ts +539 -0
- package/src/__tests__/{request-key.test.ts → integrations/request-key.test.ts} +1 -1
- package/src/__tests__/integrations/scope.test.tsx +286 -0
- package/src/__tests__/integrations/use-journey-step.test.tsx +43 -0
- package/src/__tests__/sinks/console.test.ts +103 -0
- package/src/__tests__/{datadog.test.ts → sinks/datadog.test.ts} +109 -5
- package/src/__tests__/{sinks.test.ts → sinks/sinks.test.ts} +13 -12
- package/src/config.ts +173 -41
- package/src/core/abort-error.ts +24 -0
- package/src/core/api.ts +159 -0
- package/src/{endpoint-policy.ts → core/endpoint-policy.ts} +46 -4
- package/src/core/index.ts +16 -17
- package/src/core/limits.ts +73 -0
- package/src/core/request-policy.ts +66 -0
- package/src/core/runtime.ts +560 -137
- package/src/core/sanitize.ts +177 -0
- package/src/core/step-tag.ts +90 -11
- package/src/core/types.ts +82 -26
- package/src/global.ts +466 -0
- package/src/index.ts +31 -18
- package/src/integrations/axios.ts +183 -45
- package/src/integrations/fetch.ts +127 -41
- package/src/integrations/http-report.ts +184 -0
- package/src/integrations/jquery/index.ts +37 -18
- package/src/integrations/jquery/instrument.ts +126 -42
- package/src/integrations/react/context.ts +4 -1
- package/src/integrations/react/scope.tsx +61 -10
- package/src/integrations/react/stable-config-keys.ts +28 -0
- package/src/integrations/react/use-journey-step.ts +31 -0
- package/src/integrations/request-key.ts +18 -10
- package/src/integrations/soft-skip.ts +35 -0
- package/src/sinks/console.ts +21 -0
- package/src/sinks/datadog.ts +52 -33
- package/dist/__mocks__/test-runtime.d.ts.map +0 -1
- package/dist/__tests__/ambient.test.d.ts.map +0 -1
- package/dist/__tests__/axios.test.d.ts.map +0 -1
- package/dist/__tests__/context.test.d.ts.map +0 -1
- package/dist/__tests__/datadog.test.d.ts.map +0 -1
- package/dist/__tests__/define-journey.test.d.ts.map +0 -1
- package/dist/__tests__/expose-app-journey.test.d.ts.map +0 -1
- package/dist/__tests__/fetch.test.d.ts.map +0 -1
- package/dist/__tests__/jquery.test.d.ts.map +0 -1
- package/dist/__tests__/registry.test.d.ts +0 -2
- package/dist/__tests__/registry.test.d.ts.map +0 -1
- package/dist/__tests__/request-key.test.d.ts.map +0 -1
- package/dist/__tests__/requests.test.d.ts.map +0 -1
- package/dist/__tests__/runtime.test.d.ts.map +0 -1
- package/dist/__tests__/scope.test.d.ts.map +0 -1
- package/dist/__tests__/sinks.test.d.ts.map +0 -1
- package/dist/__tests__/step.test.d.ts.map +0 -1
- package/dist/__tests__/timeouts.test.d.ts.map +0 -1
- package/dist/core/registry.d.ts +0 -50
- package/dist/core/registry.d.ts.map +0 -1
- package/dist/core/registry.js +0 -102
- package/dist/core/registry.js.map +0 -1
- package/dist/endpoint-policy.d.ts.map +0 -1
- package/dist/endpoint-policy.js +0 -39
- package/dist/endpoint-policy.js.map +0 -1
- package/src/__tests__/axios.test.ts +0 -365
- package/src/__tests__/jquery.test.ts +0 -327
- package/src/__tests__/scope.test.tsx +0 -128
- package/src/core/registry.ts +0 -162
- /package/dist/{__mocks__ → __test-utils__}/test-runtime.d.ts +0 -0
- /package/dist/__tests__/{ambient.test.d.ts → core/ambient.test.d.ts} +0 -0
- /package/dist/__tests__/{define-journey.test.d.ts → core/define-journey.test.d.ts} +0 -0
- /package/dist/__tests__/{requests.test.d.ts → core/requests.test.d.ts} +0 -0
- /package/dist/__tests__/{runtime.test.d.ts → core/runtime.test.d.ts} +0 -0
- /package/dist/__tests__/{step.test.d.ts → core/step.test.d.ts} +0 -0
- /package/dist/__tests__/{timeouts.test.d.ts → core/timeouts.test.d.ts} +0 -0
- /package/dist/__tests__/{axios.test.d.ts → integrations/axios.test.d.ts} +0 -0
- /package/dist/__tests__/{context.test.d.ts → integrations/context.test.d.ts} +0 -0
- /package/dist/__tests__/{expose-app-journey.test.d.ts → integrations/expose-app-journey.test.d.ts} +0 -0
- /package/dist/__tests__/{fetch.test.d.ts → integrations/fetch.test.d.ts} +0 -0
- /package/dist/__tests__/{jquery.test.d.ts → integrations/jquery.test.d.ts} +0 -0
- /package/dist/__tests__/{request-key.test.d.ts → integrations/request-key.test.d.ts} +0 -0
- /package/dist/__tests__/{scope.test.d.ts → integrations/scope.test.d.ts} +0 -0
- /package/dist/__tests__/{datadog.test.d.ts → sinks/datadog.test.d.ts} +0 -0
- /package/dist/__tests__/{sinks.test.d.ts → sinks/sinks.test.d.ts} +0 -0
|
@@ -1,51 +1,163 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolveStamp } from '../core';
|
|
2
|
+
import { adoptJourneyStepStamp } from '../core/step-tag';
|
|
3
|
+
import { getInstrumentedAxiosSet, registerInstrumentRecord } from '../global';
|
|
4
|
+
import { isCanceledError, onHttpComplete } from './http-report';
|
|
2
5
|
import { requestKey } from './request-key';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
import { acceptInstrumentTarget } from './soft-skip';
|
|
7
|
+
const axiosStamps = new WeakMap();
|
|
8
|
+
function stampFromConfig(config) {
|
|
9
|
+
var _axiosStamps_get;
|
|
10
|
+
if (!config) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
return (_axiosStamps_get = axiosStamps.get(config)) !== null && _axiosStamps_get !== void 0 ? _axiosStamps_get : adoptJourneyStepStamp(config.stamp);
|
|
14
|
+
}
|
|
15
|
+
function reportTiming(instance, config, status, aborted, extras) {
|
|
16
|
+
var _config_journeyStart, _ref;
|
|
17
|
+
if (!config || config.ignore === true) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const stamp = stampFromConfig(config);
|
|
21
|
+
if (!stamp || stamp.ignore) {
|
|
7
22
|
return;
|
|
8
23
|
}
|
|
9
24
|
const start = (_config_journeyStart = config.journeyStart) !== null && _config_journeyStart !== void 0 ? _config_journeyStart : globalThis.performance.now();
|
|
10
25
|
const key = requestKey(config.url, config.baseURL, instance.defaults.baseURL);
|
|
11
|
-
|
|
26
|
+
onHttpComplete({
|
|
27
|
+
stamp,
|
|
28
|
+
status,
|
|
29
|
+
durationMs: globalThis.performance.now() - start,
|
|
30
|
+
requestKey: key,
|
|
31
|
+
aborted,
|
|
32
|
+
url: config.url,
|
|
33
|
+
method: config.method,
|
|
34
|
+
meta: config,
|
|
35
|
+
request: (_ref = extras === null || extras === void 0 ? void 0 : extras.request) !== null && _ref !== void 0 ? _ref : config,
|
|
36
|
+
response: extras === null || extras === void 0 ? void 0 : extras.response,
|
|
37
|
+
error: extras === null || extras === void 0 ? void 0 : extras.error
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function isAxiosLikeInstance(value) {
|
|
41
|
+
var _instance_interceptors_request, _instance_interceptors, _instance_interceptors_response, _instance_interceptors1;
|
|
42
|
+
if (value == null || typeof value !== 'object' && typeof value !== 'function') {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
const instance = value;
|
|
46
|
+
return instance.defaults != null && typeof instance.defaults === 'object' && typeof ((_instance_interceptors = instance.interceptors) === null || _instance_interceptors === void 0 ? void 0 : (_instance_interceptors_request = _instance_interceptors.request) === null || _instance_interceptors_request === void 0 ? void 0 : _instance_interceptors_request.use) === 'function' && typeof ((_instance_interceptors1 = instance.interceptors) === null || _instance_interceptors1 === void 0 ? void 0 : (_instance_interceptors_response = _instance_interceptors1.response) === null || _instance_interceptors_response === void 0 ? void 0 : _instance_interceptors_response.use) === 'function';
|
|
47
|
+
}
|
|
48
|
+
function ejectInterceptor(interceptors, id) {
|
|
49
|
+
if (id === undefined || typeof (interceptors === null || interceptors === void 0 ? void 0 : interceptors.eject) !== 'function') {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
interceptors.eject(id);
|
|
54
|
+
return true;
|
|
55
|
+
} catch (unused) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
12
58
|
}
|
|
13
|
-
const instrumented = new WeakSet();
|
|
14
59
|
/**
|
|
15
60
|
* Wire an axios instance into the journey engine (observation-only).
|
|
16
61
|
* Call once per instance (idempotent). Register any async request interceptors
|
|
17
62
|
* (e.g. auth) before this call so this one runs first.
|
|
63
|
+
* Soft-skips when `instance` is missing or not axios-like — hosts can pass axios as-is.
|
|
64
|
+
* Returns a restore function that disposes handlers (they no-op after restore).
|
|
18
65
|
*/ export function instrumentAxios(instance) {
|
|
19
|
-
|
|
20
|
-
|
|
66
|
+
const axios = acceptInstrumentTarget('Axios', instance, isAxiosLikeInstance);
|
|
67
|
+
const instrumented = getInstrumentedAxiosSet();
|
|
68
|
+
if (!axios || instrumented.has(axios)) {
|
|
69
|
+
return ()=>{};
|
|
21
70
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
71
|
+
const record = {
|
|
72
|
+
disposed: false
|
|
73
|
+
};
|
|
74
|
+
let requestId;
|
|
75
|
+
let responseId;
|
|
76
|
+
let requestAdded = false;
|
|
77
|
+
let responseAdded = false;
|
|
78
|
+
try {
|
|
79
|
+
requestId = axios.interceptors.request.use((config)=>{
|
|
80
|
+
if (record.disposed) {
|
|
81
|
+
return config;
|
|
82
|
+
}
|
|
83
|
+
const timed = config;
|
|
84
|
+
if (timed.ignore === true) {
|
|
85
|
+
return config;
|
|
86
|
+
}
|
|
87
|
+
const stamp = resolveStamp(timed.stamp);
|
|
88
|
+
if (stamp) {
|
|
89
|
+
axiosStamps.set(timed, stamp);
|
|
90
|
+
/*
|
|
91
|
+
* Keep config.stamp so later interceptors that clone/spread config
|
|
92
|
+
* still carry attribution. JourneyStepStamp.toJSON keeps this safe
|
|
93
|
+
* for AxiosError serialization.
|
|
94
|
+
*/ }
|
|
95
|
+
if ((stamp === null || stamp === void 0 ? void 0 : stamp.ignore) === true) {
|
|
96
|
+
return config;
|
|
97
|
+
}
|
|
98
|
+
if (stamp) {
|
|
99
|
+
timed.journeyStart = globalThis.performance.now();
|
|
100
|
+
}
|
|
101
|
+
return config;
|
|
102
|
+
});
|
|
103
|
+
requestAdded = true;
|
|
104
|
+
responseId = axios.interceptors.response.use((response)=>{
|
|
105
|
+
if (!record.disposed) {
|
|
106
|
+
reportTiming(axios, response === null || response === void 0 ? void 0 : response.config, response === null || response === void 0 ? void 0 : response.status, false, {
|
|
107
|
+
request: response === null || response === void 0 ? void 0 : response.config,
|
|
108
|
+
response
|
|
109
|
+
});
|
|
32
110
|
}
|
|
111
|
+
return response;
|
|
112
|
+
}, (error)=>{
|
|
113
|
+
if (!record.disposed) {
|
|
114
|
+
var _axiosError_response;
|
|
115
|
+
const axiosError = error;
|
|
116
|
+
reportTiming(axios, axiosError === null || axiosError === void 0 ? void 0 : axiosError.config, axiosError === null || axiosError === void 0 ? void 0 : (_axiosError_response = axiosError.response) === null || _axiosError_response === void 0 ? void 0 : _axiosError_response.status, isCanceledError(error), {
|
|
117
|
+
request: axiosError === null || axiosError === void 0 ? void 0 : axiosError.config,
|
|
118
|
+
response: axiosError === null || axiosError === void 0 ? void 0 : axiosError.response,
|
|
119
|
+
error
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
/*
|
|
123
|
+
* Passthrough original reason (AxiosError or test doubles). Do not wrap —
|
|
124
|
+
* callers match on .response / AxiosError identity.
|
|
125
|
+
*/ return Promise.reject(error);
|
|
126
|
+
});
|
|
127
|
+
responseAdded = true;
|
|
128
|
+
instrumented.add(axios);
|
|
129
|
+
} catch (err) {
|
|
130
|
+
const unwound = (!responseAdded || ejectInterceptor(axios.interceptors.response, responseId)) && (!requestAdded || ejectInterceptor(axios.interceptors.request, requestId));
|
|
131
|
+
if (!unwound) {
|
|
132
|
+
/*
|
|
133
|
+
* Interceptors may still be registered and cannot be ejected. Mark disposed
|
|
134
|
+
* and keep the WeakSet slot, but register a restore so teardownJourney can
|
|
135
|
+
* unmark and a later retry can proceed.
|
|
136
|
+
*/ record.disposed = true;
|
|
137
|
+
instrumented.add(axios);
|
|
138
|
+
const restore = ()=>{
|
|
139
|
+
record.disposed = true;
|
|
140
|
+
instrumented.delete(axios);
|
|
141
|
+
};
|
|
142
|
+
record.restore = restore;
|
|
143
|
+
registerInstrumentRecord(record);
|
|
144
|
+
// eslint-disable-next-line no-console -- soft-fail host wiring
|
|
145
|
+
console.error('[journey] instrumentAxios failed', err);
|
|
146
|
+
return restore;
|
|
33
147
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
*/ return Promise.reject(error);
|
|
48
|
-
});
|
|
148
|
+
// eslint-disable-next-line no-console -- soft-fail host wiring
|
|
149
|
+
console.error('[journey] instrumentAxios failed', err);
|
|
150
|
+
return ()=>{};
|
|
151
|
+
}
|
|
152
|
+
const restore = ()=>{
|
|
153
|
+
record.disposed = true;
|
|
154
|
+
ejectInterceptor(axios.interceptors.request, requestId);
|
|
155
|
+
ejectInterceptor(axios.interceptors.response, responseId);
|
|
156
|
+
instrumented.delete(axios);
|
|
157
|
+
};
|
|
158
|
+
record.restore = restore;
|
|
159
|
+
registerInstrumentRecord(record);
|
|
160
|
+
return restore;
|
|
49
161
|
}
|
|
50
162
|
|
|
51
163
|
//# sourceMappingURL=axios.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/integrations/axios.ts"],"sourcesContent":["import {\n activeJourneyStep,\n JourneyStepTag,\n reportBackendRequest,\n resolveJourneyStep,\n} from '../core';\nimport { requestKey, type JourneyTimedMeta } from './request-key';\n\n/**\n * Minimal structural axios surface. No axios dependency — works across 0.31 and 1.x.\n */\nexport interface AxiosLikeInstance {\n defaults: { baseURL?: string };\n interceptors: {\n request: { use(onFulfilled: (config: any) => any): unknown };\n response: {\n use(onFulfilled: (response: any) => any, onRejected?: (error: any) => any): unknown;\n };\n };\n}\n\ntype TimedConfig = JourneyTimedMeta & { url?: string; baseURL?: string };\n\nfunction reportTiming(\n instance: AxiosLikeInstance,\n config: TimedConfig | undefined,\n status: number | undefined\n): void {\n const step = resolveJourneyStep(config?.journeyStep);\n if (!step || !config) {\n return;\n }\n const start = config.journeyStart ?? globalThis.performance.now();\n const key = requestKey(config.url, config.baseURL, instance.defaults.baseURL);\n reportBackendRequest(step, status, globalThis.performance.now() - start, key);\n}\n\nconst instrumented = new WeakSet<object>();\n\n/**\n * Wire an axios instance into the journey engine (observation-only).\n * Call once per instance (idempotent). Register any async request interceptors\n * (e.g. auth) before this call so this one runs first.\n */\nexport function instrumentAxios(instance: AxiosLikeInstance): void {\n if (instrumented.has(instance as object)) {\n return;\n }\n instrumented.add(instance as object);\n\n instance.interceptors.request.use(config => {\n const timed = config as TimedConfig;\n /*\n * Prefer explicit step.tag(); else soft ambient (null when 0 or 2+ steps in flight).\n * Box ambient steps in JourneyStepTag — raw StepRecord is circular and blows axios mergeConfig.\n */\n if (!resolveJourneyStep(timed.journeyStep)) {\n const step = activeJourneyStep();\n if (step) {\n timed.journeyStep = new JourneyStepTag(step);\n }\n }\n timed.journeyStart = globalThis.performance.now();\n return config;\n });\n\n instance.interceptors.response.use(\n response => {\n reportTiming(instance, response?.config as TimedConfig | undefined, response?.status);\n return response;\n },\n (error: unknown) => {\n const axiosError = error as {\n config?: TimedConfig;\n response?: { status?: number };\n };\n reportTiming(instance, axiosError?.config, axiosError?.response?.status);\n /*\n * Passthrough original reason (AxiosError or test doubles). Do not wrap —\n * callers match on .response / AxiosError identity.\n */\n return Promise.reject(error as Error);\n }\n );\n}\n"],"names":["activeJourneyStep","JourneyStepTag","reportBackendRequest","resolveJourneyStep","requestKey","reportTiming","instance","config","status","step","journeyStep","start","journeyStart","globalThis","performance","now","key","url","baseURL","defaults","instrumented","WeakSet","instrumentAxios","has","add","interceptors","request","use","timed","response","error","axiosError","Promise","reject"],"mappings":"AAAA,SACIA,iBAAiB,EACjBC,cAAc,EACdC,oBAAoB,EACpBC,kBAAkB,QACf,UAAU;AACjB,SAASC,UAAU,QAA+B,gBAAgB;AAiBlE,SAASC,aACLC,QAA2B,EAC3BC,MAA+B,EAC/BC,MAA0B;QAMZD;IAJd,MAAME,OAAON,mBAAmBI,mBAAAA,6BAAAA,OAAQG,WAAW;IACnD,IAAI,CAACD,QAAQ,CAACF,QAAQ;QAClB;IACJ;IACA,MAAMI,SAAQJ,uBAAAA,OAAOK,YAAY,cAAnBL,kCAAAA,uBAAuBM,WAAWC,WAAW,CAACC,GAAG;IAC/D,MAAMC,MAAMZ,WAAWG,OAAOU,GAAG,EAAEV,OAAOW,OAAO,EAAEZ,SAASa,QAAQ,CAACD,OAAO;IAC5EhB,qBAAqBO,MAAMD,QAAQK,WAAWC,WAAW,CAACC,GAAG,KAAKJ,OAAOK;AAC7E;AAEA,MAAMI,eAAe,IAAIC;AAEzB;;;;CAIC,GACD,OAAO,SAASC,gBAAgBhB,QAA2B;IACvD,IAAIc,aAAaG,GAAG,CAACjB,WAAqB;QACtC;IACJ;IACAc,aAAaI,GAAG,CAAClB;IAEjBA,SAASmB,YAAY,CAACC,OAAO,CAACC,GAAG,CAACpB,CAAAA;QAC9B,MAAMqB,QAAQrB;QACd;;;SAGC,GACD,IAAI,CAACJ,mBAAmByB,MAAMlB,WAAW,GAAG;YACxC,MAAMD,OAAOT;YACb,IAAIS,MAAM;gBACNmB,MAAMlB,WAAW,GAAG,IAAIT,eAAeQ;YAC3C;QACJ;QACAmB,MAAMhB,YAAY,GAAGC,WAAWC,WAAW,CAACC,GAAG;QAC/C,OAAOR;IACX;IAEAD,SAASmB,YAAY,CAACI,QAAQ,CAACF,GAAG,CAC9BE,CAAAA;QACIxB,aAAaC,UAAUuB,qBAAAA,+BAAAA,SAAUtB,MAAM,EAA6BsB,qBAAAA,+BAAAA,SAAUrB,MAAM;QACpF,OAAOqB;IACX,GACA,CAACC;YAK8CC;QAJ3C,MAAMA,aAAaD;QAInBzB,aAAaC,UAAUyB,uBAAAA,iCAAAA,WAAYxB,MAAM,EAAEwB,uBAAAA,kCAAAA,uBAAAA,WAAYF,QAAQ,cAApBE,2CAAAA,qBAAsBvB,MAAM;QACvE;;;aAGC,GACD,OAAOwB,QAAQC,MAAM,CAACH;IAC1B;AAER"}
|
|
1
|
+
{"version":3,"sources":["../../src/integrations/axios.ts"],"sourcesContent":["import { resolveStamp } from '../core';\nimport { adoptJourneyStepStamp, type JourneyStepStamp } from '../core/step-tag';\nimport {\n getInstrumentedAxiosSet,\n registerInstrumentRecord,\n type InstrumentRecord,\n} from '../global';\nimport { isCanceledError, onHttpComplete } from './http-report';\nimport { requestKey, type JourneyTimedMeta } from './request-key';\nimport { acceptInstrumentTarget } from './soft-skip';\n\n/**\n * Minimal structural axios surface. No axios dependency — works across 0.31 and 1.x.\n */\nexport interface AxiosLikeInstance {\n defaults: { baseURL?: string };\n interceptors: {\n request: {\n use(onFulfilled: (config: any) => any): unknown;\n eject?(id: unknown): void;\n };\n response: {\n use(onFulfilled: (response: any) => any, onRejected?: (error: any) => any): unknown;\n eject?(id: unknown): void;\n };\n };\n}\n\ntype TimedConfig = JourneyTimedMeta & { url?: string; baseURL?: string; method?: string };\n\nconst axiosStamps = new WeakMap<object, JourneyStepStamp>();\n\nfunction stampFromConfig(config: TimedConfig | undefined): JourneyStepStamp | undefined {\n if (!config) {\n return undefined;\n }\n return axiosStamps.get(config) ?? adoptJourneyStepStamp(config.stamp);\n}\n\nfunction reportTiming(\n instance: AxiosLikeInstance,\n config: TimedConfig | undefined,\n status: number | undefined,\n aborted: boolean,\n extras?: { request?: unknown; response?: unknown; error?: unknown }\n): void {\n if (!config || config.ignore === true) {\n return;\n }\n const stamp = stampFromConfig(config);\n if (!stamp || stamp.ignore) {\n return;\n }\n const start = config.journeyStart ?? globalThis.performance.now();\n const key = requestKey(config.url, config.baseURL, instance.defaults.baseURL);\n onHttpComplete({\n stamp,\n status,\n durationMs: globalThis.performance.now() - start,\n requestKey: key,\n aborted,\n url: config.url,\n method: config.method,\n meta: config,\n request: extras?.request ?? config,\n response: extras?.response,\n error: extras?.error,\n });\n}\n\nfunction isAxiosLikeInstance(value: unknown): value is AxiosLikeInstance {\n if (value == null || (typeof value !== 'object' && typeof value !== 'function')) {\n return false;\n }\n const instance = value as AxiosLikeInstance;\n return (\n instance.defaults != null &&\n typeof instance.defaults === 'object' &&\n typeof instance.interceptors?.request?.use === 'function' &&\n typeof instance.interceptors?.response?.use === 'function'\n );\n}\n\nfunction ejectInterceptor(\n interceptors: { eject?: (id: unknown) => void } | undefined,\n id: unknown\n): boolean {\n if (id === undefined || typeof interceptors?.eject !== 'function') {\n return false;\n }\n try {\n interceptors.eject(id);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Wire an axios instance into the journey engine (observation-only).\n * Call once per instance (idempotent). Register any async request interceptors\n * (e.g. auth) before this call so this one runs first.\n * Soft-skips when `instance` is missing or not axios-like — hosts can pass axios as-is.\n * Returns a restore function that disposes handlers (they no-op after restore).\n */\nexport function instrumentAxios(instance: AxiosLikeInstance | null | undefined): () => void {\n const axios = acceptInstrumentTarget('Axios', instance, isAxiosLikeInstance);\n const instrumented = getInstrumentedAxiosSet();\n if (!axios || instrumented.has(axios as object)) {\n return () => {};\n }\n\n const record: InstrumentRecord = { disposed: false };\n let requestId: unknown;\n let responseId: unknown;\n let requestAdded = false;\n let responseAdded = false;\n\n try {\n requestId = axios.interceptors.request.use(config => {\n if (record.disposed) {\n return config;\n }\n const timed = config as TimedConfig;\n if (timed.ignore === true) {\n return config;\n }\n const stamp = resolveStamp(timed.stamp);\n if (stamp) {\n axiosStamps.set(timed, stamp);\n /*\n * Keep config.stamp so later interceptors that clone/spread config\n * still carry attribution. JourneyStepStamp.toJSON keeps this safe\n * for AxiosError serialization.\n */\n }\n if (stamp?.ignore === true) {\n return config;\n }\n if (stamp) {\n timed.journeyStart = globalThis.performance.now();\n }\n return config;\n });\n requestAdded = true;\n\n responseId = axios.interceptors.response.use(\n response => {\n if (!record.disposed) {\n reportTiming(\n axios,\n response?.config as TimedConfig | undefined,\n response?.status,\n false,\n { request: response?.config, response }\n );\n }\n return response;\n },\n (error: unknown) => {\n if (!record.disposed) {\n const axiosError = error as {\n config?: TimedConfig;\n response?: { status?: number };\n };\n reportTiming(\n axios,\n axiosError?.config,\n axiosError?.response?.status,\n isCanceledError(error),\n {\n request: axiosError?.config,\n response: axiosError?.response,\n error,\n }\n );\n }\n /*\n * Passthrough original reason (AxiosError or test doubles). Do not wrap —\n * callers match on .response / AxiosError identity.\n */\n return Promise.reject(error as Error);\n }\n );\n responseAdded = true;\n instrumented.add(axios as object);\n } catch (err) {\n const unwound =\n (!responseAdded || ejectInterceptor(axios.interceptors.response, responseId)) &&\n (!requestAdded || ejectInterceptor(axios.interceptors.request, requestId));\n if (!unwound) {\n /*\n * Interceptors may still be registered and cannot be ejected. Mark disposed\n * and keep the WeakSet slot, but register a restore so teardownJourney can\n * unmark and a later retry can proceed.\n */\n record.disposed = true;\n instrumented.add(axios as object);\n const restore = () => {\n record.disposed = true;\n instrumented.delete(axios as object);\n };\n record.restore = restore;\n registerInstrumentRecord(record);\n // eslint-disable-next-line no-console -- soft-fail host wiring\n console.error('[journey] instrumentAxios failed', err);\n return restore;\n }\n // eslint-disable-next-line no-console -- soft-fail host wiring\n console.error('[journey] instrumentAxios failed', err);\n return () => {};\n }\n\n const restore = () => {\n record.disposed = true;\n ejectInterceptor(axios.interceptors.request, requestId);\n ejectInterceptor(axios.interceptors.response, responseId);\n instrumented.delete(axios as object);\n };\n record.restore = restore;\n registerInstrumentRecord(record);\n return restore;\n}\n"],"names":["resolveStamp","adoptJourneyStepStamp","getInstrumentedAxiosSet","registerInstrumentRecord","isCanceledError","onHttpComplete","requestKey","acceptInstrumentTarget","axiosStamps","WeakMap","stampFromConfig","config","undefined","get","stamp","reportTiming","instance","status","aborted","extras","ignore","start","journeyStart","globalThis","performance","now","key","url","baseURL","defaults","durationMs","method","meta","request","response","error","isAxiosLikeInstance","value","interceptors","use","ejectInterceptor","id","eject","instrumentAxios","axios","instrumented","has","record","disposed","requestId","responseId","requestAdded","responseAdded","timed","set","axiosError","Promise","reject","add","err","unwound","restore","delete","console"],"mappings":"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"}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/** Minimal structural fetch shape. */
|
|
2
|
-
|
|
2
|
+
type FetchLike = (input: any, init?: any) => Promise<{
|
|
3
3
|
status: number;
|
|
4
4
|
}>;
|
|
5
5
|
/** Holder of a `fetch` property — globalThis by default, or a scoped object. */
|
|
6
|
-
|
|
6
|
+
interface FetchTarget {
|
|
7
7
|
fetch: FetchLike;
|
|
8
8
|
}
|
|
9
9
|
export interface InstrumentFetchOptions {
|
|
10
10
|
/** Object whose `fetch` is wrapped. Defaults to globalThis. */
|
|
11
|
-
target?: FetchTarget;
|
|
11
|
+
target?: FetchTarget | null;
|
|
12
12
|
/** Home API base URL for request-key origin resolution. */
|
|
13
13
|
baseURL?: string;
|
|
14
14
|
}
|
|
@@ -16,6 +16,8 @@ export interface InstrumentFetchOptions {
|
|
|
16
16
|
* Wrap a target's fetch so requests inside a journeyStep are attributed and timed.
|
|
17
17
|
* Call once per target (idempotent — second call is a no-op). Returns a restore
|
|
18
18
|
* function that puts the original fetch back. Observation-only.
|
|
19
|
+
* Soft-skips when the resolved target is missing or has no callable `fetch`.
|
|
19
20
|
*/
|
|
20
|
-
export declare function instrumentFetch(options?: InstrumentFetchOptions): () => void;
|
|
21
|
+
export declare function instrumentFetch(options?: InstrumentFetchOptions | null): () => void;
|
|
22
|
+
export {};
|
|
21
23
|
//# sourceMappingURL=fetch.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/integrations/fetch.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/integrations/fetch.ts"],"names":[],"mappings":"AAWA,sCAAsC;AACtC,KAAK,SAAS,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEzE,gFAAgF;AAChF,UAAU,WAAW;IACjB,KAAK,EAAE,SAAS,CAAC;CACpB;AAED,MAAM,WAAW,sBAAsB;IACnC,+DAA+D;IAC/D,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5B,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAoCD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,IAAI,GAAG,MAAM,IAAI,CAoHnF"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolveStamp } from '../core';
|
|
2
|
+
import { getInstrumentedFetchSet, registerInstrumentRecord, setFetchRestore } from '../global';
|
|
3
|
+
import { isAbortError, onHttpComplete } from './http-report';
|
|
2
4
|
import { requestKey } from './request-key';
|
|
3
|
-
|
|
5
|
+
import { acceptInstrumentTarget } from './soft-skip';
|
|
4
6
|
const originals = new WeakMap();
|
|
5
7
|
function resolveUrl(input) {
|
|
6
8
|
if (typeof input === 'string') {
|
|
@@ -14,50 +16,127 @@ function resolveUrl(input) {
|
|
|
14
16
|
}
|
|
15
17
|
return undefined;
|
|
16
18
|
}
|
|
19
|
+
/** Fetch defaults to GET; `init.method` overrides a Request's method. */ function resolveMethod(input, init) {
|
|
20
|
+
if (typeof (init === null || init === void 0 ? void 0 : init.method) === 'string' && init.method !== '') {
|
|
21
|
+
return init.method;
|
|
22
|
+
}
|
|
23
|
+
if (typeof (input === null || input === void 0 ? void 0 : input.method) === 'string' && input.method !== '') {
|
|
24
|
+
return input.method;
|
|
25
|
+
}
|
|
26
|
+
return 'GET';
|
|
27
|
+
}
|
|
28
|
+
function isFetchTarget(value) {
|
|
29
|
+
return value != null && (typeof value === 'object' || typeof value === 'function') && typeof value.fetch === 'function';
|
|
30
|
+
}
|
|
17
31
|
/**
|
|
18
32
|
* Wrap a target's fetch so requests inside a journeyStep are attributed and timed.
|
|
19
33
|
* Call once per target (idempotent — second call is a no-op). Returns a restore
|
|
20
34
|
* function that puts the original fetch back. Observation-only.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
35
|
+
* Soft-skips when the resolved target is missing or has no callable `fetch`.
|
|
36
|
+
*/ export function instrumentFetch(options) {
|
|
37
|
+
if (options === null) {
|
|
38
|
+
acceptInstrumentTarget('Fetch', null, isFetchTarget);
|
|
39
|
+
return ()=>{};
|
|
40
|
+
}
|
|
41
|
+
const opts = options !== null && options !== void 0 ? options : {};
|
|
42
|
+
const candidate = opts.target !== undefined ? opts.target : globalThis;
|
|
43
|
+
const target = acceptInstrumentTarget('Fetch', candidate, isFetchTarget);
|
|
44
|
+
if (!target) {
|
|
45
|
+
return ()=>{};
|
|
46
|
+
}
|
|
24
47
|
const key = target;
|
|
48
|
+
const instrumented = getInstrumentedFetchSet();
|
|
25
49
|
if (instrumented.has(key)) {
|
|
26
50
|
return ()=>{};
|
|
27
51
|
}
|
|
28
52
|
const original = target.fetch;
|
|
29
53
|
originals.set(key, original);
|
|
30
54
|
instrumented.add(key);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
// Prefer explicit step.tag(); else soft ambient.
|
|
34
|
-
const explicit = resolveJourneyStep(init === null || init === void 0 ? void 0 : init.journeyStep);
|
|
35
|
-
const step = explicit !== null && explicit !== void 0 ? explicit : activeJourneyStep();
|
|
36
|
-
if (!step) {
|
|
37
|
-
return original.call(target, input, init);
|
|
38
|
-
}
|
|
39
|
-
const start = globalThis.performance.now();
|
|
40
|
-
const endpointKey = requestKey(resolveUrl(input), undefined, options.baseURL);
|
|
41
|
-
const promise = original.call(target, input, init);
|
|
42
|
-
return promise.then((response)=>{
|
|
43
|
-
reportBackendRequest(step, response.status, globalThis.performance.now() - start, endpointKey);
|
|
44
|
-
return response;
|
|
45
|
-
}, (error)=>{
|
|
46
|
-
reportBackendRequest(step, undefined, globalThis.performance.now() - start, endpointKey);
|
|
47
|
-
// Passthrough original rejection reason; do not wrap (identity / AbortError).
|
|
48
|
-
return Promise.reject(error);
|
|
49
|
-
});
|
|
55
|
+
const record = {
|
|
56
|
+
disposed: false
|
|
50
57
|
};
|
|
51
|
-
|
|
52
|
-
|
|
58
|
+
try {
|
|
59
|
+
// Call through `target` — native fetch throws "Illegal invocation" with the wrong receiver.
|
|
60
|
+
const wrapped = (input, init)=>{
|
|
61
|
+
if (record.disposed) {
|
|
62
|
+
return original.call(target, input, init);
|
|
63
|
+
}
|
|
64
|
+
// Prefer explicit step.stamp(); else auto-attribute when enabled.
|
|
65
|
+
const meta = init;
|
|
66
|
+
if ((meta === null || meta === void 0 ? void 0 : meta.ignore) === true) {
|
|
67
|
+
return original.call(target, input, init);
|
|
68
|
+
}
|
|
69
|
+
const stamp = resolveStamp(meta === null || meta === void 0 ? void 0 : meta.stamp);
|
|
70
|
+
if (!stamp || stamp.ignore) {
|
|
71
|
+
return original.call(target, input, init);
|
|
72
|
+
}
|
|
73
|
+
const start = globalThis.performance.now();
|
|
74
|
+
const url = resolveUrl(input);
|
|
75
|
+
const method = resolveMethod(input, init);
|
|
76
|
+
const endpointKey = requestKey(url, undefined, opts.baseURL);
|
|
77
|
+
const promise = original.call(target, input, init);
|
|
78
|
+
return promise.then((response)=>{
|
|
79
|
+
if (!record.disposed) {
|
|
80
|
+
onHttpComplete({
|
|
81
|
+
stamp,
|
|
82
|
+
status: response.status,
|
|
83
|
+
durationMs: globalThis.performance.now() - start,
|
|
84
|
+
requestKey: endpointKey,
|
|
85
|
+
url,
|
|
86
|
+
method,
|
|
87
|
+
meta,
|
|
88
|
+
request: init !== null && init !== void 0 ? init : input,
|
|
89
|
+
response
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return response;
|
|
93
|
+
}, (error)=>{
|
|
94
|
+
if (!record.disposed) {
|
|
95
|
+
onHttpComplete({
|
|
96
|
+
stamp,
|
|
97
|
+
status: undefined,
|
|
98
|
+
durationMs: globalThis.performance.now() - start,
|
|
99
|
+
requestKey: endpointKey,
|
|
100
|
+
aborted: isAbortError(error),
|
|
101
|
+
url,
|
|
102
|
+
method,
|
|
103
|
+
meta,
|
|
104
|
+
request: init !== null && init !== void 0 ? init : input,
|
|
105
|
+
error
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
// Passthrough original rejection reason; do not wrap (identity / AbortError).
|
|
109
|
+
return Promise.reject(error);
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
target.fetch = wrapped;
|
|
113
|
+
} catch (err) {
|
|
114
|
+
target.fetch = original;
|
|
115
|
+
instrumented.delete(key);
|
|
116
|
+
originals.delete(key);
|
|
117
|
+
// eslint-disable-next-line no-console -- soft-fail host wiring
|
|
118
|
+
console.error('[journey] instrumentFetch failed', err);
|
|
119
|
+
return ()=>{};
|
|
120
|
+
}
|
|
121
|
+
const restore = ()=>{
|
|
53
122
|
var _originals_get;
|
|
54
123
|
if (!instrumented.has(key)) {
|
|
55
124
|
return;
|
|
56
125
|
}
|
|
126
|
+
record.disposed = true;
|
|
57
127
|
target.fetch = (_originals_get = originals.get(key)) !== null && _originals_get !== void 0 ? _originals_get : original;
|
|
58
128
|
instrumented.delete(key);
|
|
59
129
|
originals.delete(key);
|
|
130
|
+
if (target === globalThis) {
|
|
131
|
+
setFetchRestore(undefined);
|
|
132
|
+
}
|
|
60
133
|
};
|
|
134
|
+
record.restore = restore;
|
|
135
|
+
registerInstrumentRecord(record);
|
|
136
|
+
if (target === globalThis) {
|
|
137
|
+
setFetchRestore(restore);
|
|
138
|
+
}
|
|
139
|
+
return restore;
|
|
61
140
|
}
|
|
62
141
|
|
|
63
142
|
//# sourceMappingURL=fetch.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/integrations/fetch.ts"],"sourcesContent":["import { activeJourneyStep, reportBackendRequest, resolveJourneyStep } from '../core';\nimport { requestKey, type JourneyTimedMeta } from './request-key';\n\n/** Minimal structural fetch shape. */\nexport type FetchLike = (input: any, init?: any) => Promise<{ status: number }>;\n\n/** Holder of a `fetch` property — globalThis by default, or a scoped object. */\nexport interface FetchTarget {\n fetch: FetchLike;\n}\n\nexport interface InstrumentFetchOptions {\n /** Object whose `fetch` is wrapped. Defaults to globalThis. */\n target?: FetchTarget;\n /** Home API base URL for request-key origin resolution. */\n baseURL?: string;\n}\n\nconst instrumented = new WeakSet<object>();\nconst originals = new WeakMap<object, FetchLike>();\n\nfunction resolveUrl(input: any): string | undefined {\n if (typeof input === 'string') {\n return input;\n }\n if (input && typeof input.url === 'string') {\n return input.url;\n }\n if (input && typeof input.href === 'string') {\n return input.href;\n }\n return undefined;\n}\n\n/**\n * Wrap a target's fetch so requests inside a journeyStep are attributed and timed.\n * Call once per target (idempotent — second call is a no-op). Returns a restore\n * function that puts the original fetch back. Observation-only.\n */\nexport function instrumentFetch(options: InstrumentFetchOptions = {}): () => void {\n const target = options.target ?? (globalThis as unknown as FetchTarget);\n const key = target as object;\n\n if (instrumented.has(key)) {\n return () => {};\n }\n\n const original = target.fetch;\n originals.set(key, original);\n instrumented.add(key);\n\n // Call through `target` — native fetch throws \"Illegal invocation\" with the wrong receiver.\n const wrapped: FetchLike = (input, init) => {\n // Prefer explicit step.tag(); else soft ambient.\n const explicit = resolveJourneyStep((init as JourneyTimedMeta | undefined)?.journeyStep);\n const step = explicit ?? activeJourneyStep();\n if (!step) {\n return original.call(target, input, init);\n }\n\n const start = globalThis.performance.now();\n const endpointKey = requestKey(resolveUrl(input), undefined, options.baseURL);\n const promise = original.call(target, input, init);\n return promise.then(\n response => {\n reportBackendRequest(\n step,\n response.status,\n globalThis.performance.now() - start,\n endpointKey\n );\n return response;\n },\n (error: unknown) => {\n reportBackendRequest(\n step,\n undefined,\n globalThis.performance.now() - start,\n endpointKey\n );\n // Passthrough original rejection reason; do not wrap (identity / AbortError).\n return Promise.reject(error as Error);\n }\n );\n };\n\n target.fetch = wrapped;\n\n return () => {\n if (!instrumented.has(key)) {\n return;\n }\n target.fetch = originals.get(key) ?? original;\n instrumented.delete(key);\n originals.delete(key);\n };\n}\n"],"names":["activeJourneyStep","reportBackendRequest","resolveJourneyStep","requestKey","instrumented","WeakSet","originals","WeakMap","resolveUrl","input","url","href","undefined","instrumentFetch","options","target","globalThis","key","has","original","fetch","set","add","wrapped","init","explicit","journeyStep","step","call","start","performance","now","endpointKey","baseURL","promise","then","response","status","error","Promise","reject","get","delete"],"mappings":"AAAA,SAASA,iBAAiB,EAAEC,oBAAoB,EAAEC,kBAAkB,QAAQ,UAAU;AACtF,SAASC,UAAU,QAA+B,gBAAgB;AAiBlE,MAAMC,eAAe,IAAIC;AACzB,MAAMC,YAAY,IAAIC;AAEtB,SAASC,WAAWC,KAAU;IAC1B,IAAI,OAAOA,UAAU,UAAU;QAC3B,OAAOA;IACX;IACA,IAAIA,SAAS,OAAOA,MAAMC,GAAG,KAAK,UAAU;QACxC,OAAOD,MAAMC,GAAG;IACpB;IACA,IAAID,SAAS,OAAOA,MAAME,IAAI,KAAK,UAAU;QACzC,OAAOF,MAAME,IAAI;IACrB;IACA,OAAOC;AACX;AAEA;;;;CAIC,GACD,OAAO,SAASC,gBAAgBC,UAAkC,CAAC,CAAC;QACjDA;IAAf,MAAMC,UAASD,kBAAAA,QAAQC,MAAM,cAAdD,6BAAAA,kBAAmBE;IAClC,MAAMC,MAAMF;IAEZ,IAAIX,aAAac,GAAG,CAACD,MAAM;QACvB,OAAO,KAAO;IAClB;IAEA,MAAME,WAAWJ,OAAOK,KAAK;IAC7Bd,UAAUe,GAAG,CAACJ,KAAKE;IACnBf,aAAakB,GAAG,CAACL;IAEjB,4FAA4F;IAC5F,MAAMM,UAAqB,CAACd,OAAOe;QAC/B,iDAAiD;QACjD,MAAMC,WAAWvB,mBAAoBsB,iBAAAA,2BAAD,AAACA,KAAuCE,WAAW;QACvF,MAAMC,OAAOF,qBAAAA,sBAAAA,WAAYzB;QACzB,IAAI,CAAC2B,MAAM;YACP,OAAOR,SAASS,IAAI,CAACb,QAAQN,OAAOe;QACxC;QAEA,MAAMK,QAAQb,WAAWc,WAAW,CAACC,GAAG;QACxC,MAAMC,cAAc7B,WAAWK,WAAWC,QAAQG,WAAWE,QAAQmB,OAAO;QAC5E,MAAMC,UAAUf,SAASS,IAAI,CAACb,QAAQN,OAAOe;QAC7C,OAAOU,QAAQC,IAAI,CACfC,CAAAA;YACInC,qBACI0B,MACAS,SAASC,MAAM,EACfrB,WAAWc,WAAW,CAACC,GAAG,KAAKF,OAC/BG;YAEJ,OAAOI;QACX,GACA,CAACE;YACGrC,qBACI0B,MACAf,WACAI,WAAWc,WAAW,CAACC,GAAG,KAAKF,OAC/BG;YAEJ,8EAA8E;YAC9E,OAAOO,QAAQC,MAAM,CAACF;QAC1B;IAER;IAEAvB,OAAOK,KAAK,GAAGG;IAEf,OAAO;YAIYjB;QAHf,IAAI,CAACF,aAAac,GAAG,CAACD,MAAM;YACxB;QACJ;QACAF,OAAOK,KAAK,IAAGd,iBAAAA,UAAUmC,GAAG,CAACxB,kBAAdX,4BAAAA,iBAAsBa;QACrCf,aAAasC,MAAM,CAACzB;QACpBX,UAAUoC,MAAM,CAACzB;IACrB;AACJ"}
|
|
1
|
+
{"version":3,"sources":["../../src/integrations/fetch.ts"],"sourcesContent":["import { resolveStamp } from '../core';\nimport {\n getInstrumentedFetchSet,\n registerInstrumentRecord,\n setFetchRestore,\n type InstrumentRecord,\n} from '../global';\nimport { isAbortError, onHttpComplete } from './http-report';\nimport { requestKey, type JourneyTimedMeta } from './request-key';\nimport { acceptInstrumentTarget } from './soft-skip';\n\n/** Minimal structural fetch shape. */\ntype FetchLike = (input: any, init?: any) => Promise<{ status: number }>;\n\n/** Holder of a `fetch` property — globalThis by default, or a scoped object. */\ninterface FetchTarget {\n fetch: FetchLike;\n}\n\nexport interface InstrumentFetchOptions {\n /** Object whose `fetch` is wrapped. Defaults to globalThis. */\n target?: FetchTarget | null;\n /** Home API base URL for request-key origin resolution. */\n baseURL?: string;\n}\n\nconst originals = new WeakMap<object, FetchLike>();\n\nfunction resolveUrl(input: any): string | undefined {\n if (typeof input === 'string') {\n return input;\n }\n if (input && typeof input.url === 'string') {\n return input.url;\n }\n if (input && typeof input.href === 'string') {\n return input.href;\n }\n return undefined;\n}\n\n/** Fetch defaults to GET; `init.method` overrides a Request's method. */\nfunction resolveMethod(input: any, init?: any): string {\n if (typeof init?.method === 'string' && init.method !== '') {\n return init.method;\n }\n if (typeof input?.method === 'string' && input.method !== '') {\n return input.method;\n }\n return 'GET';\n}\n\nfunction isFetchTarget(value: unknown): value is FetchTarget {\n return (\n value != null &&\n (typeof value === 'object' || typeof value === 'function') &&\n typeof (value as FetchTarget).fetch === 'function'\n );\n}\n\n/**\n * Wrap a target's fetch so requests inside a journeyStep are attributed and timed.\n * Call once per target (idempotent — second call is a no-op). Returns a restore\n * function that puts the original fetch back. Observation-only.\n * Soft-skips when the resolved target is missing or has no callable `fetch`.\n */\nexport function instrumentFetch(options?: InstrumentFetchOptions | null): () => void {\n if (options === null) {\n acceptInstrumentTarget('Fetch', null, isFetchTarget);\n return () => {};\n }\n\n const opts = options ?? {};\n const candidate =\n opts.target !== undefined ? opts.target : (globalThis as unknown as FetchTarget);\n const target = acceptInstrumentTarget('Fetch', candidate, isFetchTarget);\n if (!target) {\n return () => {};\n }\n\n const key = target as object;\n const instrumented = getInstrumentedFetchSet();\n\n if (instrumented.has(key)) {\n return () => {};\n }\n\n const original = target.fetch;\n originals.set(key, original);\n instrumented.add(key);\n\n const record: InstrumentRecord = { disposed: false };\n\n try {\n // Call through `target` — native fetch throws \"Illegal invocation\" with the wrong receiver.\n const wrapped: FetchLike = (input, init) => {\n if (record.disposed) {\n return original.call(target, input, init);\n }\n // Prefer explicit step.stamp(); else auto-attribute when enabled.\n const meta = init as JourneyTimedMeta | undefined;\n if (meta?.ignore === true) {\n return original.call(target, input, init);\n }\n const stamp = resolveStamp(meta?.stamp);\n if (!stamp || stamp.ignore) {\n return original.call(target, input, init);\n }\n\n const start = globalThis.performance.now();\n const url = resolveUrl(input);\n const method = resolveMethod(input, init);\n const endpointKey = requestKey(url, undefined, opts.baseURL);\n const promise = original.call(target, input, init);\n return promise.then(\n response => {\n if (!record.disposed) {\n onHttpComplete({\n stamp,\n status: response.status,\n durationMs: globalThis.performance.now() - start,\n requestKey: endpointKey,\n url,\n method,\n meta,\n request: init ?? input,\n response,\n });\n }\n return response;\n },\n (error: unknown) => {\n if (!record.disposed) {\n onHttpComplete({\n stamp,\n status: undefined,\n durationMs: globalThis.performance.now() - start,\n requestKey: endpointKey,\n aborted: isAbortError(error),\n url,\n method,\n meta,\n request: init ?? input,\n error,\n });\n }\n // Passthrough original rejection reason; do not wrap (identity / AbortError).\n return Promise.reject(error as Error);\n }\n );\n };\n\n target.fetch = wrapped;\n } catch (err) {\n target.fetch = original;\n instrumented.delete(key);\n originals.delete(key);\n // eslint-disable-next-line no-console -- soft-fail host wiring\n console.error('[journey] instrumentFetch failed', err);\n return () => {};\n }\n\n const restore = () => {\n if (!instrumented.has(key)) {\n return;\n }\n record.disposed = true;\n target.fetch = originals.get(key) ?? original;\n instrumented.delete(key);\n originals.delete(key);\n if (target === (globalThis as unknown as FetchTarget)) {\n setFetchRestore(undefined);\n }\n };\n\n record.restore = restore;\n registerInstrumentRecord(record);\n if (target === (globalThis as unknown as FetchTarget)) {\n setFetchRestore(restore);\n }\n\n return restore;\n}\n"],"names":["resolveStamp","getInstrumentedFetchSet","registerInstrumentRecord","setFetchRestore","isAbortError","onHttpComplete","requestKey","acceptInstrumentTarget","originals","WeakMap","resolveUrl","input","url","href","undefined","resolveMethod","init","method","isFetchTarget","value","fetch","instrumentFetch","options","opts","candidate","target","globalThis","key","instrumented","has","original","set","add","record","disposed","wrapped","call","meta","ignore","stamp","start","performance","now","endpointKey","baseURL","promise","then","response","status","durationMs","request","error","aborted","Promise","reject","err","delete","console","restore","get"],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { JourneyStepStamp } from '../core/step-tag';
|
|
2
|
+
import type { JourneyTimedMeta } from './request-key';
|
|
3
|
+
export interface HttpCompleteReport {
|
|
4
|
+
stamp: JourneyStepStamp | undefined;
|
|
5
|
+
status?: number;
|
|
6
|
+
durationMs: number;
|
|
7
|
+
requestKey: string;
|
|
8
|
+
aborted?: boolean;
|
|
9
|
+
url?: string;
|
|
10
|
+
method?: string;
|
|
11
|
+
/** Per-request literal overrides from JourneyTimedMeta. */
|
|
12
|
+
meta?: JourneyTimedMeta;
|
|
13
|
+
/** Transport request (axios config, fetch input/init, jQuery settings). */
|
|
14
|
+
request?: unknown;
|
|
15
|
+
/** Transport response (axios response, fetch Response, jqXHR). */
|
|
16
|
+
response?: unknown;
|
|
17
|
+
/** Rejection reason when the transport failed. */
|
|
18
|
+
error?: unknown;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Attribute a finished HTTP call: abort / 4xx policies, else 5xx+latency via reportBackendRequest.
|
|
22
|
+
* Ignored endpoint prefixes never change the journey verdict.
|
|
23
|
+
* 4xx with policy `'continue'` (default) still scores latency.
|
|
24
|
+
*/
|
|
25
|
+
export declare function onHttpComplete(report: HttpCompleteReport): void;
|
|
26
|
+
export { isAbortError, isCanceledError } from '../core/abort-error';
|
|
27
|
+
//# sourceMappingURL=http-report.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-report.d.ts","sourceRoot":"","sources":["../../src/integrations/http-report.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,MAAM,WAAW,kBAAkB;IAC/B,KAAK,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kDAAkD;IAClD,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB;AAwCD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI,CA2G/D;AAED,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { MAX_ATTRIBUTED_REQUEST_KEYS } from '../core/limits';
|
|
2
|
+
import { isJourneyRequestPolicy, resolveRequestPolicy } from '../core/request-policy';
|
|
3
|
+
import { reportBackendRequest } from '../core/api';
|
|
4
|
+
import { canRouteHttpToJourney, resolveJourneyEngine } from '../core/runtime';
|
|
5
|
+
function applyPolicy(policy, step, reason, status) {
|
|
6
|
+
var _step, _reason;
|
|
7
|
+
if (policy === 'continue') {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const journey = step.journey;
|
|
11
|
+
if (!journey || journey.closed) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const engine = resolveJourneyEngine(journey);
|
|
15
|
+
if (policy === 'exclude') {
|
|
16
|
+
engine.exclude();
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
// 'bad'
|
|
20
|
+
step.outcome = 'bad';
|
|
21
|
+
(_reason = (_step = step).reason) !== null && _reason !== void 0 ? _reason : _step.reason = reason;
|
|
22
|
+
if (status !== undefined) {
|
|
23
|
+
step.httpStatus = status;
|
|
24
|
+
}
|
|
25
|
+
engine.failJourney(reason);
|
|
26
|
+
}
|
|
27
|
+
function httpScoreReason(report) {
|
|
28
|
+
if (report.aborted) {
|
|
29
|
+
return 'request-aborted';
|
|
30
|
+
}
|
|
31
|
+
const status = report.status;
|
|
32
|
+
if (status !== undefined && status >= 400 && status < 500) {
|
|
33
|
+
return 'request-client-error';
|
|
34
|
+
}
|
|
35
|
+
return 'request-error';
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Attribute a finished HTTP call: abort / 4xx policies, else 5xx+latency via reportBackendRequest.
|
|
39
|
+
* Ignored endpoint prefixes never change the journey verdict.
|
|
40
|
+
* 4xx with policy `'continue'` (default) still scores latency.
|
|
41
|
+
*/ export function onHttpComplete(report) {
|
|
42
|
+
const { stamp, status, durationMs, requestKey, aborted, url, method, meta, request, response, error } = report;
|
|
43
|
+
if (!stamp) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const step = stamp.step;
|
|
47
|
+
if (!(step === null || step === void 0 ? void 0 : step.journey)) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
/*
|
|
51
|
+
* Unstamped foreign journeys (older package copies) cannot be closed from
|
|
52
|
+
* this bundle — do not write onHttpAborted / step.outcome on them.
|
|
53
|
+
*/ if (!canRouteHttpToJourney(step.journey)) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const engine = resolveJourneyEngine(step.journey);
|
|
57
|
+
if ((meta === null || meta === void 0 ? void 0 : meta.ignore) === true || stamp.ignore === true) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (aborted) {
|
|
61
|
+
step.onHttpAborted = true;
|
|
62
|
+
}
|
|
63
|
+
if (requestKey) {
|
|
64
|
+
var _step, _attributedRequestKeys;
|
|
65
|
+
(_attributedRequestKeys = (_step = step).attributedRequestKeys) !== null && _attributedRequestKeys !== void 0 ? _attributedRequestKeys : _step.attributedRequestKeys = [];
|
|
66
|
+
if (step.attributedRequestKeys.length < MAX_ATTRIBUTED_REQUEST_KEYS && !step.attributedRequestKeys.includes(requestKey)) {
|
|
67
|
+
step.attributedRequestKeys.push(requestKey);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (requestKey && engine.shouldIgnoreRequest(requestKey)) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const ctx = {
|
|
74
|
+
requestKey,
|
|
75
|
+
status,
|
|
76
|
+
aborted: aborted === true,
|
|
77
|
+
url,
|
|
78
|
+
method,
|
|
79
|
+
journeyName: step.journey.name,
|
|
80
|
+
stepName: step.name
|
|
81
|
+
};
|
|
82
|
+
const score = stamp.score;
|
|
83
|
+
if (score) {
|
|
84
|
+
let policy;
|
|
85
|
+
try {
|
|
86
|
+
const scored = score({
|
|
87
|
+
...ctx,
|
|
88
|
+
durationMs,
|
|
89
|
+
request,
|
|
90
|
+
response,
|
|
91
|
+
error
|
|
92
|
+
});
|
|
93
|
+
if (isJourneyRequestPolicy(scored)) {
|
|
94
|
+
policy = scored;
|
|
95
|
+
}
|
|
96
|
+
} catch (unused) {
|
|
97
|
+
policy = undefined;
|
|
98
|
+
}
|
|
99
|
+
if (policy !== undefined) {
|
|
100
|
+
applyPolicy(policy, step, httpScoreReason(report), status);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (aborted) {
|
|
105
|
+
const override = meta === null || meta === void 0 ? void 0 : meta.httpAbortedRequests;
|
|
106
|
+
const policy = isJourneyRequestPolicy(override) ? override : resolveRequestPolicy(engine.getHttpAbortedRequestsPolicy(), 'continue', ctx);
|
|
107
|
+
applyPolicy(policy, step, 'request-aborted');
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (status !== undefined && status >= 400 && status < 500) {
|
|
111
|
+
const override = meta === null || meta === void 0 ? void 0 : meta.httpClientErrorRequests;
|
|
112
|
+
const policy = isJourneyRequestPolicy(override) ? override : resolveRequestPolicy(engine.getHttpClientErrorRequestsPolicy(), 'continue', ctx);
|
|
113
|
+
if (policy !== 'continue') {
|
|
114
|
+
applyPolicy(policy, step, 'request-client-error', status);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
// 'continue' does not count 4xx as an error; still score latency below.
|
|
118
|
+
}
|
|
119
|
+
reportBackendRequest(step, status, durationMs, requestKey);
|
|
120
|
+
}
|
|
121
|
+
export { isAbortError, isCanceledError } from '../core/abort-error';
|
|
122
|
+
|
|
123
|
+
//# sourceMappingURL=http-report.js.map
|