@servicetitan/journey 2.1.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 -1
- package/dist/__test-utils__/test-runtime.js +9 -2
- package/dist/__test-utils__/test-runtime.js.map +1 -1
- package/dist/__tests__/core/sanitize.test.d.ts +2 -0
- package/dist/__tests__/core/sanitize.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/host-mfe.test.d.ts +2 -0
- package/dist/__tests__/integrations/host-mfe.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/config.d.ts +57 -13
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +87 -24
- 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 +22 -9
- package/dist/core/api.d.ts.map +1 -1
- package/dist/core/api.js +77 -30
- package/dist/core/api.js.map +1 -1
- package/dist/core/endpoint-policy.d.ts +14 -4
- package/dist/core/endpoint-policy.d.ts.map +1 -1
- package/dist/core/endpoint-policy.js +32 -1
- package/dist/core/endpoint-policy.js.map +1 -1
- package/dist/core/index.d.ts +4 -3
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -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 +65 -15
- package/dist/core/runtime.d.ts.map +1 -1
- package/dist/core/runtime.js +403 -126
- 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 +25 -6
- package/dist/core/step-tag.d.ts.map +1 -1
- package/dist/core/step-tag.js +56 -10
- package/dist/core/step-tag.js.map +1 -1
- package/dist/core/types.d.ts +73 -25
- 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 +91 -13
- package/dist/global.d.ts.map +1 -1
- package/dist/global.js +235 -25
- package/dist/global.js.map +1 -1
- package/dist/index.d.ts +7 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -6
- 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 +3 -2
- 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 +8 -4
- package/dist/integrations/jquery/index.d.ts.map +1 -1
- package/dist/integrations/jquery/index.js +26 -8
- 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 +46 -8
- 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.map +1 -1
- package/dist/sinks/console.js +2 -1
- package/dist/sinks/console.js.map +1 -1
- package/dist/sinks/datadog.d.ts +1 -1
- package/dist/sinks/datadog.d.ts.map +1 -1
- package/dist/sinks/datadog.js +16 -20
- package/dist/sinks/datadog.js.map +1 -1
- package/package.json +2 -2
- package/src/__test-utils__/test-runtime.ts +9 -2
- package/src/__tests__/config.test.ts +150 -31
- package/src/__tests__/core/ambient.test.ts +16 -10
- package/src/__tests__/core/api.test.ts +7 -7
- package/src/__tests__/core/define-journey.test.ts +28 -11
- package/src/__tests__/core/requests.test.ts +49 -31
- package/src/__tests__/core/runtime.test.ts +67 -20
- package/src/__tests__/core/sanitize.test.ts +69 -0
- package/src/__tests__/core/step.test.ts +132 -36
- package/src/__tests__/core/timeouts.test.ts +69 -4
- package/src/__tests__/global.test.ts +44 -3
- package/src/__tests__/hardening.test.ts +613 -0
- package/src/__tests__/integrations/axios.test.ts +247 -27
- package/src/__tests__/integrations/expose-app-journey.test.ts +9 -3
- package/src/__tests__/integrations/fetch.test.ts +153 -19
- package/src/__tests__/integrations/host-mfe.test.ts +548 -0
- package/src/__tests__/integrations/jquery.test.ts +209 -34
- package/src/__tests__/integrations/scope.test.tsx +160 -2
- package/src/__tests__/integrations/use-journey-step.test.tsx +43 -0
- package/src/__tests__/sinks/console.test.ts +9 -4
- package/src/__tests__/sinks/datadog.test.ts +47 -0
- package/src/config.ts +162 -38
- package/src/core/abort-error.ts +24 -0
- package/src/core/api.ts +83 -33
- package/src/core/endpoint-policy.ts +46 -4
- package/src/core/index.ts +12 -4
- package/src/core/limits.ts +73 -0
- package/src/core/request-policy.ts +66 -0
- package/src/core/runtime.ts +507 -133
- package/src/core/sanitize.ts +177 -0
- package/src/core/step-tag.ts +82 -12
- package/src/core/types.ts +79 -25
- package/src/global.ts +312 -42
- package/src/index.ts +17 -17
- package/src/integrations/axios.ts +183 -45
- package/src/integrations/fetch.ts +125 -39
- package/src/integrations/http-report.ts +184 -0
- package/src/integrations/jquery/index.ts +34 -10
- package/src/integrations/jquery/instrument.ts +126 -42
- package/src/integrations/react/context.ts +4 -1
- package/src/integrations/react/scope.tsx +59 -9
- 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 +2 -1
- package/src/sinks/datadog.ts +23 -29
|
@@ -1,18 +1,27 @@
|
|
|
1
1
|
/** @jest-environment jsdom */
|
|
2
|
-
import {
|
|
2
|
+
import { resolveStamp, JourneyStepStamp } from '../../core';
|
|
3
|
+
import type { StepRecord } from '../../core/types';
|
|
3
4
|
import { instrumentFetch, type InstrumentFetchOptions } from '../../integrations/fetch';
|
|
5
|
+
import { onHttpComplete } from '../../integrations/http-report';
|
|
4
6
|
import { requestKey } from '../../integrations/request-key';
|
|
5
7
|
|
|
6
8
|
jest.mock('../../core', () => ({
|
|
7
9
|
...jest.requireActual('../../core'),
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
resolveStamp: jest.fn(),
|
|
11
|
+
}));
|
|
12
|
+
jest.mock('../../integrations/http-report', () => ({
|
|
13
|
+
...jest.requireActual('../../integrations/http-report'),
|
|
14
|
+
onHttpComplete: jest.fn(),
|
|
10
15
|
}));
|
|
11
16
|
jest.mock('../../integrations/request-key');
|
|
12
17
|
|
|
13
18
|
describe('[journey] instrumentFetch', () => {
|
|
14
19
|
const KEY = 'resolved-endpoint-key';
|
|
15
|
-
const
|
|
20
|
+
const record = {
|
|
21
|
+
name: 'load',
|
|
22
|
+
journey: { name: 'j', closed: false },
|
|
23
|
+
} as unknown as StepRecord;
|
|
24
|
+
const stamp = new JourneyStepStamp(record);
|
|
16
25
|
|
|
17
26
|
let target: { fetch: jest.Mock };
|
|
18
27
|
let originalFetch: jest.Mock;
|
|
@@ -33,10 +42,9 @@ describe('[journey] instrumentFetch', () => {
|
|
|
33
42
|
init = undefined;
|
|
34
43
|
options = { target };
|
|
35
44
|
|
|
36
|
-
jest.mocked(
|
|
45
|
+
jest.mocked(resolveStamp).mockReturnValue(stamp);
|
|
37
46
|
jest.mocked(requestKey).mockReturnValue(KEY);
|
|
38
47
|
|
|
39
|
-
// Deterministic clock: each globalThis.performance.now() advances by 100ms.
|
|
40
48
|
now = 1_000;
|
|
41
49
|
jest.spyOn(globalThis.performance, 'now').mockImplementation(() => (now += 100));
|
|
42
50
|
});
|
|
@@ -70,9 +78,53 @@ describe('[journey] instrumentFetch', () => {
|
|
|
70
78
|
|
|
71
79
|
await target.fetch(input, init);
|
|
72
80
|
expect(originalFetch).toHaveBeenCalledTimes(1);
|
|
73
|
-
expect(
|
|
81
|
+
expect(onHttpComplete).toHaveBeenCalledTimes(1);
|
|
74
82
|
});
|
|
75
83
|
|
|
84
|
+
test('warns and soft-skips when options are null', () => {
|
|
85
|
+
const warn = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
86
|
+
const error = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
87
|
+
|
|
88
|
+
const restore = instrumentFetch(null);
|
|
89
|
+
|
|
90
|
+
expect(() => restore()).not.toThrow();
|
|
91
|
+
expect(warn).toHaveBeenCalledWith(
|
|
92
|
+
expect.stringContaining('[journey] instrumentFetch: skipped — missing')
|
|
93
|
+
);
|
|
94
|
+
expect(error).not.toHaveBeenCalled();
|
|
95
|
+
expect(target.fetch).toBe(originalFetch);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('warns and soft-skips when target is null', () => {
|
|
99
|
+
const warn = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
100
|
+
const error = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
101
|
+
|
|
102
|
+
const restore = instrumentFetch({ target: null });
|
|
103
|
+
|
|
104
|
+
expect(() => restore()).not.toThrow();
|
|
105
|
+
expect(warn).toHaveBeenCalledWith(
|
|
106
|
+
expect.stringContaining('[journey] instrumentFetch: skipped — missing')
|
|
107
|
+
);
|
|
108
|
+
expect(error).not.toHaveBeenCalled();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test.each([{}, { fetch: 1 }, { fetch: undefined }])(
|
|
112
|
+
'errors and soft-skips when target has invalid shape (%p)',
|
|
113
|
+
bad => {
|
|
114
|
+
const warn = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
115
|
+
const error = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
116
|
+
|
|
117
|
+
const restore = instrumentFetch({ target: bad as never });
|
|
118
|
+
|
|
119
|
+
expect(() => restore()).not.toThrow();
|
|
120
|
+
expect(error).toHaveBeenCalledWith(
|
|
121
|
+
expect.stringContaining('[journey] instrumentFetch: skipped — invalid shape (')
|
|
122
|
+
);
|
|
123
|
+
expect(error.mock.calls[0]).toHaveLength(1);
|
|
124
|
+
expect(warn).not.toHaveBeenCalled();
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
|
|
76
128
|
test('delegates to the original fetch with the same arguments', async () => {
|
|
77
129
|
init = { method: 'POST' };
|
|
78
130
|
|
|
@@ -90,16 +142,51 @@ describe('[journey] instrumentFetch', () => {
|
|
|
90
142
|
test('reports the finished request against the active step', async () => {
|
|
91
143
|
await subject();
|
|
92
144
|
|
|
93
|
-
expect(
|
|
145
|
+
expect(onHttpComplete).toHaveBeenCalledWith(
|
|
146
|
+
expect.objectContaining({
|
|
147
|
+
stamp,
|
|
148
|
+
status: 200,
|
|
149
|
+
durationMs: 100,
|
|
150
|
+
requestKey: KEY,
|
|
151
|
+
method: 'GET',
|
|
152
|
+
})
|
|
153
|
+
);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test('reports method from init', async () => {
|
|
157
|
+
init = { method: 'POST' };
|
|
158
|
+
|
|
159
|
+
await subject();
|
|
160
|
+
|
|
161
|
+
expect(onHttpComplete).toHaveBeenCalledWith(
|
|
162
|
+
expect.objectContaining({ method: 'POST', status: 200, requestKey: KEY })
|
|
163
|
+
);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test('reports the Request method when init has none', async () => {
|
|
167
|
+
input = new Request('https://api.example.com/orders', { method: 'PUT' });
|
|
168
|
+
|
|
169
|
+
await subject();
|
|
170
|
+
|
|
171
|
+
expect(onHttpComplete).toHaveBeenCalledWith(expect.objectContaining({ method: 'PUT' }));
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test('prefers init.method over the Request method', async () => {
|
|
175
|
+
input = new Request('https://api.example.com/orders', { method: 'PUT' });
|
|
176
|
+
init = { method: 'PATCH' };
|
|
177
|
+
|
|
178
|
+
await subject();
|
|
179
|
+
|
|
180
|
+
expect(onHttpComplete).toHaveBeenCalledWith(expect.objectContaining({ method: 'PATCH' }));
|
|
94
181
|
});
|
|
95
182
|
|
|
96
183
|
describe('when no journey step is active', () => {
|
|
97
|
-
beforeEach(() => jest.mocked(
|
|
184
|
+
beforeEach(() => jest.mocked(resolveStamp).mockReturnValue(undefined));
|
|
98
185
|
|
|
99
186
|
test('does not report the request', async () => {
|
|
100
187
|
await subject();
|
|
101
188
|
|
|
102
|
-
expect(
|
|
189
|
+
expect(onHttpComplete).not.toHaveBeenCalled();
|
|
103
190
|
});
|
|
104
191
|
|
|
105
192
|
test('still delegates to the original fetch', async () => {
|
|
@@ -112,8 +199,12 @@ describe('[journey] instrumentFetch', () => {
|
|
|
112
199
|
describe('when the active step changes before the response settles', () => {
|
|
113
200
|
beforeEach(() => {
|
|
114
201
|
originalFetch.mockImplementation(() => {
|
|
115
|
-
|
|
116
|
-
|
|
202
|
+
jest.mocked(resolveStamp).mockReturnValue(
|
|
203
|
+
new JourneyStepStamp({
|
|
204
|
+
name: 'other',
|
|
205
|
+
journey: { name: 'j2', closed: false },
|
|
206
|
+
} as unknown as StepRecord)
|
|
207
|
+
);
|
|
117
208
|
return Promise.resolve(response);
|
|
118
209
|
});
|
|
119
210
|
});
|
|
@@ -121,7 +212,9 @@ describe('[journey] instrumentFetch', () => {
|
|
|
121
212
|
test('reports against the step captured at call time', async () => {
|
|
122
213
|
await subject();
|
|
123
214
|
|
|
124
|
-
expect(
|
|
215
|
+
expect(onHttpComplete).toHaveBeenCalledWith(
|
|
216
|
+
expect.objectContaining({ stamp, status: 200, requestKey: KEY })
|
|
217
|
+
);
|
|
125
218
|
});
|
|
126
219
|
});
|
|
127
220
|
|
|
@@ -137,7 +230,15 @@ describe('[journey] instrumentFetch', () => {
|
|
|
137
230
|
test('reports the failure with an undefined status', async () => {
|
|
138
231
|
await subject().catch(() => {});
|
|
139
232
|
|
|
140
|
-
expect(
|
|
233
|
+
expect(onHttpComplete).toHaveBeenCalledWith(
|
|
234
|
+
expect.objectContaining({
|
|
235
|
+
stamp,
|
|
236
|
+
status: undefined,
|
|
237
|
+
durationMs: 100,
|
|
238
|
+
requestKey: KEY,
|
|
239
|
+
aborted: false,
|
|
240
|
+
})
|
|
241
|
+
);
|
|
141
242
|
});
|
|
142
243
|
});
|
|
143
244
|
|
|
@@ -200,18 +301,51 @@ describe('[journey] instrumentFetch', () => {
|
|
|
200
301
|
});
|
|
201
302
|
});
|
|
202
303
|
|
|
203
|
-
describe('when init carries an explicit
|
|
204
|
-
const explicit = {
|
|
304
|
+
describe('when init carries an explicit stamp', () => {
|
|
305
|
+
const explicit = new JourneyStepStamp({
|
|
306
|
+
name: 'explicit',
|
|
307
|
+
journey: { name: 'j', closed: false },
|
|
308
|
+
} as unknown as StepRecord);
|
|
205
309
|
|
|
206
310
|
beforeEach(() => {
|
|
207
|
-
init = {
|
|
208
|
-
jest.mocked(
|
|
311
|
+
init = { stamp: explicit } as RequestInit;
|
|
312
|
+
jest.mocked(resolveStamp).mockImplementation(value => (value ? explicit : stamp));
|
|
209
313
|
});
|
|
210
314
|
|
|
211
315
|
test('prefers the explicit step over ambient', async () => {
|
|
212
316
|
await subject();
|
|
213
317
|
|
|
214
|
-
expect(
|
|
318
|
+
expect(onHttpComplete).toHaveBeenCalledWith(
|
|
319
|
+
expect.objectContaining({ stamp: explicit, status: 200, requestKey: KEY })
|
|
320
|
+
);
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
describe('when init.ignore is true', () => {
|
|
325
|
+
beforeEach(() => {
|
|
326
|
+
init = { ignore: true } as RequestInit;
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
test('does not report the request', async () => {
|
|
330
|
+
await subject();
|
|
331
|
+
|
|
332
|
+
expect(onHttpComplete).not.toHaveBeenCalled();
|
|
333
|
+
expect(originalFetch).toHaveBeenCalledWith(input, init);
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
describe('when the stamp is ignore', () => {
|
|
338
|
+
beforeEach(() => {
|
|
339
|
+
jest.mocked(resolveStamp).mockReturnValue(
|
|
340
|
+
new JourneyStepStamp(record, { ignore: true })
|
|
341
|
+
);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
test('does not report the request', async () => {
|
|
345
|
+
await subject();
|
|
346
|
+
|
|
347
|
+
expect(onHttpComplete).not.toHaveBeenCalled();
|
|
348
|
+
expect(originalFetch).toHaveBeenCalledWith(input, init);
|
|
215
349
|
});
|
|
216
350
|
});
|
|
217
351
|
|