@servicetitan/journey 1.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.
Files changed (136) hide show
  1. package/README.md +7 -0
  2. package/dist/__mocks__/test-runtime.d.ts +16 -0
  3. package/dist/__mocks__/test-runtime.d.ts.map +1 -0
  4. package/dist/__tests__/ambient.test.d.ts +2 -0
  5. package/dist/__tests__/ambient.test.d.ts.map +1 -0
  6. package/dist/__tests__/axios.test.d.ts +2 -0
  7. package/dist/__tests__/axios.test.d.ts.map +1 -0
  8. package/dist/__tests__/config.test.d.ts +2 -0
  9. package/dist/__tests__/config.test.d.ts.map +1 -0
  10. package/dist/__tests__/context.test.d.ts +2 -0
  11. package/dist/__tests__/context.test.d.ts.map +1 -0
  12. package/dist/__tests__/datadog.test.d.ts +2 -0
  13. package/dist/__tests__/datadog.test.d.ts.map +1 -0
  14. package/dist/__tests__/define-journey.test.d.ts +2 -0
  15. package/dist/__tests__/define-journey.test.d.ts.map +1 -0
  16. package/dist/__tests__/expose-app-journey.test.d.ts +2 -0
  17. package/dist/__tests__/expose-app-journey.test.d.ts.map +1 -0
  18. package/dist/__tests__/fetch.test.d.ts +2 -0
  19. package/dist/__tests__/fetch.test.d.ts.map +1 -0
  20. package/dist/__tests__/jquery.test.d.ts +2 -0
  21. package/dist/__tests__/jquery.test.d.ts.map +1 -0
  22. package/dist/__tests__/registry.test.d.ts +2 -0
  23. package/dist/__tests__/registry.test.d.ts.map +1 -0
  24. package/dist/__tests__/request-key.test.d.ts +2 -0
  25. package/dist/__tests__/request-key.test.d.ts.map +1 -0
  26. package/dist/__tests__/requests.test.d.ts +2 -0
  27. package/dist/__tests__/requests.test.d.ts.map +1 -0
  28. package/dist/__tests__/runtime.test.d.ts +2 -0
  29. package/dist/__tests__/runtime.test.d.ts.map +1 -0
  30. package/dist/__tests__/scope.test.d.ts +2 -0
  31. package/dist/__tests__/scope.test.d.ts.map +1 -0
  32. package/dist/__tests__/sinks.test.d.ts +2 -0
  33. package/dist/__tests__/sinks.test.d.ts.map +1 -0
  34. package/dist/__tests__/step.test.d.ts +2 -0
  35. package/dist/__tests__/step.test.d.ts.map +1 -0
  36. package/dist/__tests__/timeouts.test.d.ts +2 -0
  37. package/dist/__tests__/timeouts.test.d.ts.map +1 -0
  38. package/dist/config.d.ts +42 -0
  39. package/dist/config.d.ts.map +1 -0
  40. package/dist/config.js +46 -0
  41. package/dist/config.js.map +1 -0
  42. package/dist/core/index.d.ts +6 -0
  43. package/dist/core/index.d.ts.map +1 -0
  44. package/dist/core/index.js +5 -0
  45. package/dist/core/index.js.map +1 -0
  46. package/dist/core/registry.d.ts +50 -0
  47. package/dist/core/registry.d.ts.map +1 -0
  48. package/dist/core/registry.js +102 -0
  49. package/dist/core/registry.js.map +1 -0
  50. package/dist/core/runtime.d.ts +51 -0
  51. package/dist/core/runtime.d.ts.map +1 -0
  52. package/dist/core/runtime.js +401 -0
  53. package/dist/core/runtime.js.map +1 -0
  54. package/dist/core/step-tag.d.ts +16 -0
  55. package/dist/core/step-tag.d.ts.map +1 -0
  56. package/dist/core/step-tag.js +35 -0
  57. package/dist/core/step-tag.js.map +1 -0
  58. package/dist/core/types.d.ts +143 -0
  59. package/dist/core/types.d.ts.map +1 -0
  60. package/dist/core/types.js +3 -0
  61. package/dist/core/types.js.map +1 -0
  62. package/dist/endpoint-policy.d.ts +29 -0
  63. package/dist/endpoint-policy.d.ts.map +1 -0
  64. package/dist/endpoint-policy.js +39 -0
  65. package/dist/endpoint-policy.js.map +1 -0
  66. package/dist/index.d.ts +15 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +19 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/integrations/axios.d.ts +23 -0
  71. package/dist/integrations/axios.d.ts.map +1 -0
  72. package/dist/integrations/axios.js +51 -0
  73. package/dist/integrations/axios.js.map +1 -0
  74. package/dist/integrations/fetch.d.ts +21 -0
  75. package/dist/integrations/fetch.d.ts.map +1 -0
  76. package/dist/integrations/fetch.js +63 -0
  77. package/dist/integrations/fetch.js.map +1 -0
  78. package/dist/integrations/jquery/index.d.ts +22 -0
  79. package/dist/integrations/jquery/index.d.ts.map +1 -0
  80. package/dist/integrations/jquery/index.js +27 -0
  81. package/dist/integrations/jquery/index.js.map +1 -0
  82. package/dist/integrations/jquery/instrument.d.ts +18 -0
  83. package/dist/integrations/jquery/instrument.d.ts.map +1 -0
  84. package/dist/integrations/jquery/instrument.js +44 -0
  85. package/dist/integrations/jquery/instrument.js.map +1 -0
  86. package/dist/integrations/react/context.d.ts +6 -0
  87. package/dist/integrations/react/context.d.ts.map +1 -0
  88. package/dist/integrations/react/context.js +14 -0
  89. package/dist/integrations/react/context.js.map +1 -0
  90. package/dist/integrations/react/scope.d.ts +13 -0
  91. package/dist/integrations/react/scope.d.ts.map +1 -0
  92. package/dist/integrations/react/scope.js +27 -0
  93. package/dist/integrations/react/scope.js.map +1 -0
  94. package/dist/integrations/request-key.d.ts +17 -0
  95. package/dist/integrations/request-key.d.ts.map +1 -0
  96. package/dist/integrations/request-key.js +39 -0
  97. package/dist/integrations/request-key.js.map +1 -0
  98. package/dist/sinks/datadog.d.ts +6 -0
  99. package/dist/sinks/datadog.d.ts.map +1 -0
  100. package/dist/sinks/datadog.js +64 -0
  101. package/dist/sinks/datadog.js.map +1 -0
  102. package/package.json +44 -0
  103. package/src/__mocks__/test-runtime.ts +37 -0
  104. package/src/__tests__/ambient.test.ts +101 -0
  105. package/src/__tests__/axios.test.ts +365 -0
  106. package/src/__tests__/config.test.ts +261 -0
  107. package/src/__tests__/context.test.tsx +84 -0
  108. package/src/__tests__/datadog.test.ts +160 -0
  109. package/src/__tests__/define-journey.test.ts +176 -0
  110. package/src/__tests__/expose-app-journey.test.ts +149 -0
  111. package/src/__tests__/fetch.test.ts +264 -0
  112. package/src/__tests__/jquery.test.ts +327 -0
  113. package/src/__tests__/registry.test.ts +265 -0
  114. package/src/__tests__/request-key.test.ts +81 -0
  115. package/src/__tests__/requests.test.ts +337 -0
  116. package/src/__tests__/runtime.test.ts +273 -0
  117. package/src/__tests__/scope.test.tsx +128 -0
  118. package/src/__tests__/sinks.test.ts +99 -0
  119. package/src/__tests__/step.test.ts +305 -0
  120. package/src/__tests__/timeouts.test.ts +226 -0
  121. package/src/config.ts +87 -0
  122. package/src/core/index.ts +39 -0
  123. package/src/core/registry.ts +162 -0
  124. package/src/core/runtime.ts +489 -0
  125. package/src/core/step-tag.ts +24 -0
  126. package/src/core/types.ts +160 -0
  127. package/src/endpoint-policy.ts +66 -0
  128. package/src/index.ts +51 -0
  129. package/src/integrations/axios.ts +85 -0
  130. package/src/integrations/fetch.ts +97 -0
  131. package/src/integrations/jquery/index.ts +62 -0
  132. package/src/integrations/jquery/instrument.ts +84 -0
  133. package/src/integrations/react/context.ts +17 -0
  134. package/src/integrations/react/scope.tsx +32 -0
  135. package/src/integrations/request-key.ts +46 -0
  136. package/src/sinks/datadog.ts +71 -0
@@ -0,0 +1,401 @@
1
+ import { longestEndpointMatch, sortEndpointsByLongestMatch } from '../endpoint-policy';
2
+ import { JourneyStepTag } from './step-tag';
3
+ const DEFAULT_REQUEST_TIMEOUTS = {
4
+ defaultMs: 4000,
5
+ endpoints: []
6
+ };
7
+ const noopStep = {
8
+ name: null,
9
+ setAttribute () {},
10
+ setTags () {},
11
+ complete () {},
12
+ fail () {},
13
+ tag: ()=>({})
14
+ };
15
+ function toName(target) {
16
+ return typeof target === 'string' ? target : target.name;
17
+ }
18
+ /**
19
+ * @internal Private — not part of the public package API.
20
+ * Build an isolated engine. Rarely needed — apps use the default via free helpers.
21
+ */ export function createJourneyRuntime(options = {}) {
22
+ var _options_sinks, _ref, _ref1;
23
+ var _options_requestTimeouts, _options_requestTimeouts1;
24
+ const journeys = new Map();
25
+ const inFlight = new Set();
26
+ let sinks = [
27
+ ...(_options_sinks = options.sinks) !== null && _options_sinks !== void 0 ? _options_sinks : []
28
+ ];
29
+ let requestTimeouts = {
30
+ defaultMs: (_ref = (_options_requestTimeouts = options.requestTimeouts) === null || _options_requestTimeouts === void 0 ? void 0 : _options_requestTimeouts.defaultMs) !== null && _ref !== void 0 ? _ref : DEFAULT_REQUEST_TIMEOUTS.defaultMs,
31
+ endpoints: sortEndpointsByLongestMatch((_ref1 = (_options_requestTimeouts1 = options.requestTimeouts) === null || _options_requestTimeouts1 === void 0 ? void 0 : _options_requestTimeouts1.endpoints) !== null && _ref1 !== void 0 ? _ref1 : DEFAULT_REQUEST_TIMEOUTS.endpoints)
32
+ };
33
+ function getInFlightSteps(journey) {
34
+ const out = [];
35
+ for (const step of inFlight){
36
+ if (step.journey === journey) {
37
+ out.push(step);
38
+ }
39
+ }
40
+ return out;
41
+ }
42
+ function enterStep(step) {
43
+ inFlight.add(step);
44
+ }
45
+ function leaveStep(step) {
46
+ inFlight.delete(step);
47
+ }
48
+ function clearCountdown(journey) {
49
+ if (journey.timer != null) {
50
+ clearTimeout(journey.timer);
51
+ journey.timer = null;
52
+ }
53
+ }
54
+ function emit(payload) {
55
+ for (const sink of sinks){
56
+ try {
57
+ sink(payload);
58
+ } catch (unused) {
59
+ // One sinking throw must not block the remaining sinks.
60
+ }
61
+ }
62
+ }
63
+ function finish(journey, outcome, reason) {
64
+ var _journey_reason;
65
+ if (journey.closed) {
66
+ return;
67
+ }
68
+ journey.closed = true;
69
+ journeys.delete(journey.name);
70
+ clearCountdown(journey);
71
+ for (const step of getInFlightSteps(journey)){
72
+ if (step.durationMs === 0) {
73
+ step.durationMs = Math.round(globalThis.performance.now() - step.startedAt);
74
+ }
75
+ }
76
+ let finalReason = (_journey_reason = journey.reason) !== null && _journey_reason !== void 0 ? _journey_reason : reason;
77
+ let finalOutcome = outcome;
78
+ const durationMs = Math.round(globalThis.performance.now() - journey.startedAt);
79
+ if (journey.timeoutMs > 0 && durationMs > journey.timeoutMs) {
80
+ finalOutcome = 'bad';
81
+ finalReason = finalReason !== null && finalReason !== void 0 ? finalReason : 'journey-timeout';
82
+ }
83
+ const steps = journey.steps.map((s)=>({
84
+ name: s.name,
85
+ startMs: Math.round(s.startedAt - journey.startedAt),
86
+ durationMs: s.durationMs,
87
+ outcome: s.outcome,
88
+ ...s.reason ? {
89
+ reason: s.reason
90
+ } : {},
91
+ ...s.httpStatus !== undefined ? {
92
+ httpStatus: s.httpStatus
93
+ } : {},
94
+ ...Object.keys(s.attributes).length ? {
95
+ attributes: s.attributes
96
+ } : {}
97
+ }));
98
+ emit({
99
+ journey: {
100
+ name: journey.name,
101
+ team: journey.team,
102
+ group: journey.group,
103
+ service: journey.service,
104
+ outcome: finalOutcome,
105
+ ...finalOutcome === 'bad' && finalReason ? {
106
+ reason: finalReason
107
+ } : {},
108
+ durationMs,
109
+ steps,
110
+ ...journey.expected ? {
111
+ expected: journey.expected
112
+ } : {},
113
+ ...Object.keys(journey.tags).length ? {
114
+ tags: journey.tags
115
+ } : {}
116
+ }
117
+ });
118
+ }
119
+ function armCountdown(journey) {
120
+ if (journey.timeoutMs <= 0) {
121
+ return;
122
+ }
123
+ journey.timer = setTimeout(()=>{
124
+ finish(journey, 'bad', 'journey-timeout');
125
+ }, journey.timeoutMs);
126
+ }
127
+ function resolveRequestTimeoutMs(journey, key) {
128
+ const own = longestEndpointMatch(journey.endpoints, key);
129
+ if (own === null || own === void 0 ? void 0 : own.timeoutMs) {
130
+ return own.timeoutMs;
131
+ }
132
+ const app = longestEndpointMatch(requestTimeouts.endpoints, key);
133
+ if (app === null || app === void 0 ? void 0 : app.timeoutMs) {
134
+ return app.timeoutMs;
135
+ }
136
+ if (journey.requestTimeoutMs != null) {
137
+ return journey.requestTimeoutMs;
138
+ }
139
+ return requestTimeouts.defaultMs;
140
+ }
141
+ function shouldIgnoreRequest(journey, key) {
142
+ var _longestEndpointMatch;
143
+ const own = longestEndpointMatch(journey.endpoints, key);
144
+ if (own) {
145
+ return own.ignore === true;
146
+ }
147
+ return ((_longestEndpointMatch = longestEndpointMatch(requestTimeouts.endpoints, key)) === null || _longestEndpointMatch === void 0 ? void 0 : _longestEndpointMatch.ignore) === true;
148
+ }
149
+ function resolveJourneyName(journey) {
150
+ if (journey == null) {
151
+ return null;
152
+ }
153
+ if (typeof journey === 'string' || !Array.isArray(journey)) {
154
+ return toName(journey);
155
+ }
156
+ return api.firstOpenJourney(journey.map(toName));
157
+ }
158
+ function createStepHandle(journeyName, step) {
159
+ return {
160
+ name: journeyName,
161
+ setAttribute (key, value) {
162
+ step.attributes[key] = value;
163
+ },
164
+ setTags (tags) {
165
+ api.setJourneyTags(journeyName, tags);
166
+ },
167
+ complete (tags) {
168
+ api.completeJourney(journeyName, tags);
169
+ },
170
+ fail (reason) {
171
+ api.failJourney(journeyName, reason);
172
+ },
173
+ // Box in JourneyStepTag — raw StepRecord is circular and blows jQuery/axios deep-merge.
174
+ tag: ()=>({
175
+ journeyStep: new JourneyStepTag(step)
176
+ })
177
+ };
178
+ }
179
+ const api = {
180
+ getRequestTimeouts () {
181
+ return requestTimeouts;
182
+ },
183
+ setRequestTimeouts (next) {
184
+ requestTimeouts = {
185
+ ...next,
186
+ endpoints: sortEndpointsByLongestMatch(next.endpoints)
187
+ };
188
+ },
189
+ setSinks (next) {
190
+ sinks = [
191
+ ...next
192
+ ];
193
+ },
194
+ addSink (sink) {
195
+ sinks.push(sink);
196
+ },
197
+ emit,
198
+ getOpenJourney (name) {
199
+ return journeys.get(name);
200
+ },
201
+ firstOpenJourney (names) {
202
+ for (const name of names){
203
+ if (journeys.has(name)) {
204
+ return name;
205
+ }
206
+ }
207
+ return null;
208
+ },
209
+ setJourneyTags (name, tags) {
210
+ const journey = name ? journeys.get(name) : undefined;
211
+ if (journey) {
212
+ Object.assign(journey.tags, tags);
213
+ }
214
+ },
215
+ startJourney (def) {
216
+ var _def_timeoutMs, _def_tags;
217
+ const existing = journeys.get(def.name);
218
+ if (existing) {
219
+ finish(existing, existing.verdict === 'bad' ? 'bad' : 'excluded');
220
+ }
221
+ const journey = {
222
+ name: def.name,
223
+ team: def.team,
224
+ group: def.group,
225
+ service: def.service,
226
+ verdict: 'good',
227
+ startedAt: globalThis.performance.now(),
228
+ timeoutMs: (_def_timeoutMs = def.timeoutMs) !== null && _def_timeoutMs !== void 0 ? _def_timeoutMs : 0,
229
+ stepTimeoutMs: def.stepTimeoutMs,
230
+ reason: null,
231
+ steps: [],
232
+ expected: def.steps ? [
233
+ ...def.steps
234
+ ] : null,
235
+ tags: {
236
+ ...(_def_tags = def.tags) !== null && _def_tags !== void 0 ? _def_tags : {}
237
+ },
238
+ requestTimeoutMs: def.requestTimeoutMs,
239
+ endpoints: sortEndpointsByLongestMatch(def.endpoints),
240
+ timer: null,
241
+ closed: false
242
+ };
243
+ journeys.set(def.name, journey);
244
+ armCountdown(journey);
245
+ },
246
+ failJourneyNow (journey, reason) {
247
+ var _journey, _reason;
248
+ if (journey.closed) {
249
+ return;
250
+ }
251
+ journey.verdict = 'bad';
252
+ (_reason = (_journey = journey).reason) !== null && _reason !== void 0 ? _reason : _journey.reason = reason;
253
+ finish(journey, 'bad');
254
+ },
255
+ failJourney (name, reason) {
256
+ const journey = journeys.get(name);
257
+ if (journey) {
258
+ api.failJourneyNow(journey, reason);
259
+ }
260
+ },
261
+ completeJourney (name, tags) {
262
+ if (!name) {
263
+ return;
264
+ }
265
+ if (tags) {
266
+ api.setJourneyTags(name, tags);
267
+ }
268
+ const journey = journeys.get(name);
269
+ if (journey) {
270
+ finish(journey, journey.verdict === 'bad' ? 'bad' : 'good');
271
+ }
272
+ },
273
+ excludeJourney (name) {
274
+ const journey = journeys.get(name);
275
+ if (journey) {
276
+ finish(journey, journey.verdict === 'bad' ? 'bad' : 'excluded');
277
+ }
278
+ },
279
+ activeJourneyStep () {
280
+ var _inFlight_values_next_value;
281
+ if (inFlight.size !== 1) {
282
+ return null;
283
+ }
284
+ return (_inFlight_values_next_value = inFlight.values().next().value) !== null && _inFlight_values_next_value !== void 0 ? _inFlight_values_next_value : null;
285
+ },
286
+ async journeyStep (journey, stepName, fn, opts) {
287
+ var _ref, _ref1;
288
+ const journeyName = resolveJourneyName(journey);
289
+ const open = journeyName ? journeys.get(journeyName) : undefined;
290
+ if (!open || !journeyName) {
291
+ return fn(noopStep);
292
+ }
293
+ const step = {
294
+ name: stepName,
295
+ startedAt: globalThis.performance.now(),
296
+ durationMs: 0,
297
+ outcome: 'good',
298
+ attributes: {},
299
+ journey: open
300
+ };
301
+ open.steps.push(step);
302
+ const handle = createStepHandle(journeyName, step);
303
+ const stepTimeoutMs = (_ref = (_ref1 = opts === null || opts === void 0 ? void 0 : opts.timeoutMs) !== null && _ref1 !== void 0 ? _ref1 : open.stepTimeoutMs) !== null && _ref !== void 0 ? _ref : 0;
304
+ let timeoutTimer = null;
305
+ if (stepTimeoutMs > 0) {
306
+ timeoutTimer = setTimeout(()=>{
307
+ var _step, _reason, _step_attributes, _timeout_ms;
308
+ if (open.closed) {
309
+ return;
310
+ }
311
+ step.outcome = 'bad';
312
+ (_reason = (_step = step).reason) !== null && _reason !== void 0 ? _reason : _step.reason = 'step-timeout';
313
+ (_timeout_ms = (_step_attributes = step.attributes).timeout_ms) !== null && _timeout_ms !== void 0 ? _timeout_ms : _step_attributes.timeout_ms = stepTimeoutMs;
314
+ step.durationMs = Math.round(globalThis.performance.now() - step.startedAt);
315
+ api.failJourneyNow(open, 'step-timeout');
316
+ }, stepTimeoutMs);
317
+ }
318
+ enterStep(step);
319
+ try {
320
+ return await fn(handle);
321
+ } catch (err) {
322
+ var _step, _reason, _step_attributes, _error;
323
+ const message = err instanceof Error ? err.message : String(err);
324
+ step.outcome = 'bad';
325
+ (_reason = (_step = step).reason) !== null && _reason !== void 0 ? _reason : _step.reason = 'step-error';
326
+ (_error = (_step_attributes = step.attributes).error) !== null && _error !== void 0 ? _error : _step_attributes.error = message;
327
+ api.failJourneyNow(open, 'step-error');
328
+ throw err;
329
+ } finally{
330
+ if (timeoutTimer != null) {
331
+ clearTimeout(timeoutTimer);
332
+ }
333
+ if (step.durationMs === 0) {
334
+ step.durationMs = Math.round(globalThis.performance.now() - step.startedAt);
335
+ }
336
+ leaveStep(step);
337
+ }
338
+ },
339
+ async journeyMountStep (journey, stepName, fn, opts) {
340
+ await Promise.resolve();
341
+ return api.journeyStep(journey, stepName, fn, opts);
342
+ },
343
+ reportBackendRequest (step, status, durationMs, requestKey) {
344
+ var _step, _reason;
345
+ if (!step) {
346
+ return;
347
+ }
348
+ const journey = step.journey;
349
+ if (journey.closed) {
350
+ return;
351
+ }
352
+ if (shouldIgnoreRequest(journey, requestKey)) {
353
+ return;
354
+ }
355
+ const failed = status === undefined || status >= 500;
356
+ const slow = durationMs > resolveRequestTimeoutMs(journey, requestKey);
357
+ if (!failed && !slow) {
358
+ return;
359
+ }
360
+ const category = failed ? 'request-error' : 'request-latency';
361
+ step.outcome = 'bad';
362
+ (_reason = (_step = step).reason) !== null && _reason !== void 0 ? _reason : _step.reason = category;
363
+ if (failed && status !== undefined) {
364
+ step.httpStatus = status;
365
+ }
366
+ api.failJourneyNow(journey, category);
367
+ },
368
+ reset (next = {}) {
369
+ var _next_sinks, _ref, _ref1;
370
+ var _next_requestTimeouts, _next_requestTimeouts1;
371
+ for (const journey of [
372
+ ...journeys.values()
373
+ ]){
374
+ clearCountdown(journey);
375
+ journey.closed = true;
376
+ }
377
+ journeys.clear();
378
+ inFlight.clear();
379
+ sinks = [
380
+ ...(_next_sinks = next.sinks) !== null && _next_sinks !== void 0 ? _next_sinks : []
381
+ ];
382
+ requestTimeouts = {
383
+ defaultMs: (_ref = (_next_requestTimeouts = next.requestTimeouts) === null || _next_requestTimeouts === void 0 ? void 0 : _next_requestTimeouts.defaultMs) !== null && _ref !== void 0 ? _ref : DEFAULT_REQUEST_TIMEOUTS.defaultMs,
384
+ endpoints: sortEndpointsByLongestMatch((_ref1 = (_next_requestTimeouts1 = next.requestTimeouts) === null || _next_requestTimeouts1 === void 0 ? void 0 : _next_requestTimeouts1.endpoints) !== null && _ref1 !== void 0 ? _ref1 : DEFAULT_REQUEST_TIMEOUTS.endpoints)
385
+ };
386
+ }
387
+ };
388
+ return api;
389
+ }
390
+ /**
391
+ * @internal Private — not part of the public package API.
392
+ * Process-wide engine used by free helpers. App code does not need to touch this.
393
+ */ export const defaultRuntime = createJourneyRuntime();
394
+ /**
395
+ * Reset the default engine (open journeys, ambient steps, sinks, request thresholds).
396
+ * Intended for tests — call in `beforeEach` / `afterEach` instead of manual cleanup.
397
+ */ export function resetJourney(options) {
398
+ defaultRuntime.reset(options);
399
+ }
400
+
401
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/runtime.ts"],"sourcesContent":["import {\n longestEndpointMatch,\n sortEndpointsByLongestMatch,\n type AppRequestTimeouts,\n} from '../endpoint-policy';\nimport { JourneyStepTag } from './step-tag';\nimport type {\n JourneyDef,\n JourneyEvent,\n JourneySink,\n JourneyState,\n JourneyStepEvent,\n JourneyStepOptions,\n JourneyStepRef,\n JourneyStepTarget,\n Outcome,\n StepHandle,\n StepRecord,\n TagValue,\n} from './types';\n\nconst DEFAULT_REQUEST_TIMEOUTS: AppRequestTimeouts = { defaultMs: 4_000, endpoints: [] };\n\nconst noopStep: StepHandle = {\n name: null,\n setAttribute() {},\n setTags() {},\n complete() {},\n fail() {},\n tag: () => ({}),\n};\n\nfunction toName(target: string | { readonly name: string }): string {\n return typeof target === 'string' ? target : target.name;\n}\n\n/** @internal Private — not part of the public package API. */\nexport interface JourneyRuntimeOptions {\n /** Initial sinks. Defaults to `[]` — package bootstrap installs Datadog on the default runtime. */\n sinks?: readonly JourneySink[];\n requestTimeouts?: AppRequestTimeouts;\n}\n\n/**\n * @internal Private — not part of the public package API.\n * Isolated journey engine (open journeys, ambient steps, sinks, request thresholds).\n * App code should keep using free helpers (`defineJourney`, `journeyStep`, …) — they\n * bind to the process default runtime. Prefer `resetJourney()` in tests.\n */\nexport interface JourneyRuntime {\n getRequestTimeouts(): AppRequestTimeouts;\n setRequestTimeouts(next: AppRequestTimeouts): void;\n\n setSinks(next: readonly JourneySink[]): void;\n addSink(sink: JourneySink): void;\n emit(payload: JourneyEvent): void;\n\n getOpenJourney(name: string): JourneyState | undefined;\n firstOpenJourney(names: readonly string[]): string | null;\n setJourneyTags(name: string | null, tags: Record<string, TagValue>): void;\n startJourney(def: JourneyDef): void;\n failJourneyNow(journey: JourneyState, reason: string): void;\n failJourney(name: string, reason: string): void;\n completeJourney(name: string | null, tags?: Record<string, TagValue>): void;\n excludeJourney(name: string): void;\n\n activeJourneyStep(): JourneyStepRef | null;\n journeyStep<T>(\n journey: JourneyStepTarget,\n stepName: string,\n fn: (step: StepHandle) => T | Promise<T>,\n opts?: JourneyStepOptions\n ): Promise<T>;\n journeyMountStep<T>(\n journey: JourneyStepTarget,\n stepName: string,\n fn: (step: StepHandle) => T | Promise<T>,\n opts?: JourneyStepOptions\n ): Promise<T>;\n\n reportBackendRequest(\n step: JourneyStepRef | undefined,\n status: number | undefined,\n durationMs: number,\n requestKey: string\n ): void;\n\n /** Close open journeys, clear ambient steps, restore default timeouts/sinks. */\n reset(options?: JourneyRuntimeOptions): void;\n}\n\n/**\n * @internal Private — not part of the public package API.\n * Build an isolated engine. Rarely needed — apps use the default via free helpers.\n */\nexport function createJourneyRuntime(options: JourneyRuntimeOptions = {}): JourneyRuntime {\n const journeys = new Map<string, JourneyState>();\n const inFlight = new Set<StepRecord>();\n let sinks: JourneySink[] = [...(options.sinks ?? [])];\n let requestTimeouts: AppRequestTimeouts = {\n defaultMs: options.requestTimeouts?.defaultMs ?? DEFAULT_REQUEST_TIMEOUTS.defaultMs,\n endpoints: sortEndpointsByLongestMatch(\n options.requestTimeouts?.endpoints ?? DEFAULT_REQUEST_TIMEOUTS.endpoints\n ),\n };\n\n function getInFlightSteps(journey: JourneyState): StepRecord[] {\n const out: StepRecord[] = [];\n for (const step of inFlight) {\n if (step.journey === journey) {\n out.push(step);\n }\n }\n return out;\n }\n\n function enterStep(step: StepRecord): void {\n inFlight.add(step);\n }\n\n function leaveStep(step: StepRecord): void {\n inFlight.delete(step);\n }\n\n function clearCountdown(journey: JourneyState): void {\n if (journey.timer != null) {\n clearTimeout(journey.timer);\n journey.timer = null;\n }\n }\n\n function emit(payload: JourneyEvent): void {\n for (const sink of sinks) {\n try {\n sink(payload);\n } catch {\n // One sinking throw must not block the remaining sinks.\n }\n }\n }\n\n function finish(journey: JourneyState, outcome: Outcome, reason?: string): void {\n if (journey.closed) {\n return;\n }\n journey.closed = true;\n journeys.delete(journey.name);\n clearCountdown(journey);\n\n for (const step of getInFlightSteps(journey)) {\n if (step.durationMs === 0) {\n step.durationMs = Math.round(globalThis.performance.now() - step.startedAt);\n }\n }\n\n let finalReason = journey.reason ?? reason;\n let finalOutcome = outcome;\n const durationMs = Math.round(globalThis.performance.now() - journey.startedAt);\n if (journey.timeoutMs > 0 && durationMs > journey.timeoutMs) {\n finalOutcome = 'bad';\n finalReason = finalReason ?? 'journey-timeout';\n }\n\n const steps: JourneyStepEvent[] = journey.steps.map(s => ({\n name: s.name,\n startMs: Math.round(s.startedAt - journey.startedAt),\n durationMs: s.durationMs,\n outcome: s.outcome,\n ...(s.reason ? { reason: s.reason } : {}),\n ...(s.httpStatus !== undefined ? { httpStatus: s.httpStatus } : {}),\n ...(Object.keys(s.attributes).length ? { attributes: s.attributes } : {}),\n }));\n\n emit({\n journey: {\n name: journey.name,\n team: journey.team,\n group: journey.group,\n service: journey.service,\n outcome: finalOutcome,\n ...(finalOutcome === 'bad' && finalReason ? { reason: finalReason } : {}),\n durationMs,\n steps,\n ...(journey.expected ? { expected: journey.expected } : {}),\n ...(Object.keys(journey.tags).length ? { tags: journey.tags } : {}),\n },\n });\n }\n\n function armCountdown(journey: JourneyState): void {\n if (journey.timeoutMs <= 0) {\n return;\n }\n journey.timer = setTimeout(() => {\n finish(journey, 'bad', 'journey-timeout');\n }, journey.timeoutMs);\n }\n\n function resolveRequestTimeoutMs(journey: JourneyState, key: string): number {\n const own = longestEndpointMatch(journey.endpoints, key);\n if (own?.timeoutMs) {\n return own.timeoutMs;\n }\n\n const app = longestEndpointMatch(requestTimeouts.endpoints, key);\n if (app?.timeoutMs) {\n return app.timeoutMs;\n }\n\n if (journey.requestTimeoutMs != null) {\n return journey.requestTimeoutMs;\n }\n\n return requestTimeouts.defaultMs;\n }\n\n function shouldIgnoreRequest(journey: JourneyState, key: string): boolean {\n const own = longestEndpointMatch(journey.endpoints, key);\n if (own) {\n return own.ignore === true;\n }\n return longestEndpointMatch(requestTimeouts.endpoints, key)?.ignore === true;\n }\n\n function resolveJourneyName(journey: JourneyStepTarget): string | null {\n if (journey == null) {\n return null;\n }\n if (typeof journey === 'string' || !Array.isArray(journey)) {\n return toName(journey as string | { readonly name: string });\n }\n return api.firstOpenJourney(journey.map(toName));\n }\n\n function createStepHandle(journeyName: string, step: StepRecord): StepHandle {\n return {\n name: journeyName,\n setAttribute(key, value) {\n step.attributes[key] = value;\n },\n setTags(tags) {\n api.setJourneyTags(journeyName, tags);\n },\n complete(tags) {\n api.completeJourney(journeyName, tags);\n },\n fail(reason) {\n api.failJourney(journeyName, reason);\n },\n // Box in JourneyStepTag — raw StepRecord is circular and blows jQuery/axios deep-merge.\n tag: () => ({ journeyStep: new JourneyStepTag(step) }),\n };\n }\n\n const api: JourneyRuntime = {\n getRequestTimeouts() {\n return requestTimeouts;\n },\n\n setRequestTimeouts(next) {\n requestTimeouts = {\n ...next,\n endpoints: sortEndpointsByLongestMatch(next.endpoints),\n };\n },\n\n setSinks(next) {\n sinks = [...next];\n },\n\n addSink(sink) {\n sinks.push(sink);\n },\n\n emit,\n\n getOpenJourney(name) {\n return journeys.get(name);\n },\n\n firstOpenJourney(names) {\n for (const name of names) {\n if (journeys.has(name)) {\n return name;\n }\n }\n return null;\n },\n\n setJourneyTags(name, tags) {\n const journey = name ? journeys.get(name) : undefined;\n if (journey) {\n Object.assign(journey.tags, tags);\n }\n },\n\n startJourney(def) {\n const existing = journeys.get(def.name);\n if (existing) {\n finish(existing, existing.verdict === 'bad' ? 'bad' : 'excluded');\n }\n\n const journey: JourneyState = {\n name: def.name,\n team: def.team,\n group: def.group,\n service: def.service,\n verdict: 'good',\n startedAt: globalThis.performance.now(),\n timeoutMs: def.timeoutMs ?? 0,\n stepTimeoutMs: def.stepTimeoutMs,\n reason: null,\n steps: [],\n expected: def.steps ? [...def.steps] : null,\n tags: { ...(def.tags ?? {}) },\n requestTimeoutMs: def.requestTimeoutMs,\n endpoints: sortEndpointsByLongestMatch(def.endpoints),\n timer: null,\n closed: false,\n };\n journeys.set(def.name, journey);\n armCountdown(journey);\n },\n\n failJourneyNow(journey, reason) {\n if (journey.closed) {\n return;\n }\n journey.verdict = 'bad';\n journey.reason ??= reason;\n finish(journey, 'bad');\n },\n\n failJourney(name, reason) {\n const journey = journeys.get(name);\n if (journey) {\n api.failJourneyNow(journey, reason);\n }\n },\n\n completeJourney(name, tags) {\n if (!name) {\n return;\n }\n if (tags) {\n api.setJourneyTags(name, tags);\n }\n const journey = journeys.get(name);\n if (journey) {\n finish(journey, journey.verdict === 'bad' ? 'bad' : 'good');\n }\n },\n\n excludeJourney(name) {\n const journey = journeys.get(name);\n if (journey) {\n finish(journey, journey.verdict === 'bad' ? 'bad' : 'excluded');\n }\n },\n\n activeJourneyStep() {\n if (inFlight.size !== 1) {\n return null;\n }\n return inFlight.values().next().value ?? null;\n },\n\n async journeyStep(journey, stepName, fn, opts) {\n const journeyName = resolveJourneyName(journey);\n const open = journeyName ? journeys.get(journeyName) : undefined;\n if (!open || !journeyName) {\n return fn(noopStep);\n }\n\n const step: StepRecord = {\n name: stepName,\n startedAt: globalThis.performance.now(),\n durationMs: 0,\n outcome: 'good',\n attributes: {},\n journey: open,\n };\n open.steps.push(step);\n\n const handle = createStepHandle(journeyName, step);\n\n const stepTimeoutMs = opts?.timeoutMs ?? open.stepTimeoutMs ?? 0;\n let timeoutTimer: ReturnType<typeof setTimeout> | null = null;\n if (stepTimeoutMs > 0) {\n timeoutTimer = setTimeout(() => {\n if (open.closed) {\n return;\n }\n step.outcome = 'bad';\n step.reason ??= 'step-timeout';\n step.attributes.timeout_ms ??= stepTimeoutMs;\n step.durationMs = Math.round(globalThis.performance.now() - step.startedAt);\n api.failJourneyNow(open, 'step-timeout');\n }, stepTimeoutMs);\n }\n\n enterStep(step);\n try {\n return await fn(handle);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n step.outcome = 'bad';\n step.reason ??= 'step-error';\n step.attributes.error ??= message;\n api.failJourneyNow(open, 'step-error');\n throw err;\n } finally {\n if (timeoutTimer != null) {\n clearTimeout(timeoutTimer);\n }\n if (step.durationMs === 0) {\n step.durationMs = Math.round(globalThis.performance.now() - step.startedAt);\n }\n leaveStep(step);\n }\n },\n\n async journeyMountStep(journey, stepName, fn, opts) {\n await Promise.resolve();\n return api.journeyStep(journey, stepName, fn, opts);\n },\n\n reportBackendRequest(step, status, durationMs, requestKey) {\n if (!step) {\n return;\n }\n const journey = step.journey;\n if (journey.closed) {\n return;\n }\n if (shouldIgnoreRequest(journey, requestKey)) {\n return;\n }\n\n const failed = status === undefined || status >= 500;\n const slow = durationMs > resolveRequestTimeoutMs(journey, requestKey);\n if (!failed && !slow) {\n return;\n }\n\n const category = failed ? 'request-error' : 'request-latency';\n\n step.outcome = 'bad';\n step.reason ??= category;\n if (failed && status !== undefined) {\n step.httpStatus = status;\n }\n\n api.failJourneyNow(journey, category);\n },\n\n reset(next = {}) {\n for (const journey of [...journeys.values()]) {\n clearCountdown(journey);\n journey.closed = true;\n }\n journeys.clear();\n inFlight.clear();\n sinks = [...(next.sinks ?? [])];\n requestTimeouts = {\n defaultMs: next.requestTimeouts?.defaultMs ?? DEFAULT_REQUEST_TIMEOUTS.defaultMs,\n endpoints: sortEndpointsByLongestMatch(\n next.requestTimeouts?.endpoints ?? DEFAULT_REQUEST_TIMEOUTS.endpoints\n ),\n };\n },\n };\n\n return api;\n}\n\n/**\n * @internal Private — not part of the public package API.\n * Process-wide engine used by free helpers. App code does not need to touch this.\n */\nexport const defaultRuntime: JourneyRuntime = createJourneyRuntime();\n\n/**\n * Reset the default engine (open journeys, ambient steps, sinks, request thresholds).\n * Intended for tests — call in `beforeEach` / `afterEach` instead of manual cleanup.\n */\nexport function resetJourney(options?: JourneyRuntimeOptions): void {\n defaultRuntime.reset(options);\n}\n"],"names":["longestEndpointMatch","sortEndpointsByLongestMatch","JourneyStepTag","DEFAULT_REQUEST_TIMEOUTS","defaultMs","endpoints","noopStep","name","setAttribute","setTags","complete","fail","tag","toName","target","createJourneyRuntime","options","journeys","Map","inFlight","Set","sinks","requestTimeouts","getInFlightSteps","journey","out","step","push","enterStep","add","leaveStep","delete","clearCountdown","timer","clearTimeout","emit","payload","sink","finish","outcome","reason","closed","durationMs","Math","round","globalThis","performance","now","startedAt","finalReason","finalOutcome","timeoutMs","steps","map","s","startMs","httpStatus","undefined","Object","keys","attributes","length","team","group","service","expected","tags","armCountdown","setTimeout","resolveRequestTimeoutMs","key","own","app","requestTimeoutMs","shouldIgnoreRequest","ignore","resolveJourneyName","Array","isArray","api","firstOpenJourney","createStepHandle","journeyName","value","setJourneyTags","completeJourney","failJourney","journeyStep","getRequestTimeouts","setRequestTimeouts","next","setSinks","addSink","getOpenJourney","get","names","has","assign","startJourney","def","existing","verdict","stepTimeoutMs","set","failJourneyNow","excludeJourney","activeJourneyStep","size","values","stepName","fn","opts","open","handle","timeoutTimer","timeout_ms","err","message","Error","String","error","journeyMountStep","Promise","resolve","reportBackendRequest","status","requestKey","failed","slow","category","reset","clear","defaultRuntime","resetJourney"],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { JourneyStepRef, StepRecord } from './types';
2
+ /**
3
+ * Non-plain wrapper for {@link StepHandle.tag}.
4
+ *
5
+ * jQuery `ajaxSetup` / axios `mergeConfig` deep-merge plain objects. A raw
6
+ * {@link StepRecord} is circular (`step.journey.steps → step`), so putting it
7
+ * on ajax/axios options causes "Maximum call stack size exceeded". Class
8
+ * instances are assigned by reference instead of recursed.
9
+ */
10
+ export declare class JourneyStepTag {
11
+ readonly step: StepRecord;
12
+ constructor(step: StepRecord);
13
+ }
14
+ /** Unwrap `step.tag().journeyStep` or a JourneyStepTag / StepRecord stamp. */
15
+ export declare function resolveJourneyStep(value: unknown): JourneyStepRef | undefined;
16
+ //# sourceMappingURL=step-tag.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-tag.d.ts","sourceRoot":"","sources":["../../src/core/step-tag.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1D;;;;;;;GAOG;AACH,qBAAa,cAAc;IACX,QAAQ,CAAC,IAAI,EAAE,UAAU;gBAAhB,IAAI,EAAE,UAAU;CACxC;AAED,8EAA8E;AAC9E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS,CAQ7E"}
@@ -0,0 +1,35 @@
1
+ function _define_property(obj, key, value) {
2
+ if (key in obj) {
3
+ Object.defineProperty(obj, key, {
4
+ value: value,
5
+ enumerable: true,
6
+ configurable: true,
7
+ writable: true
8
+ });
9
+ } else obj[key] = value;
10
+ return obj;
11
+ }
12
+ /**
13
+ * Non-plain wrapper for {@link StepHandle.tag}.
14
+ *
15
+ * jQuery `ajaxSetup` / axios `mergeConfig` deep-merge plain objects. A raw
16
+ * {@link StepRecord} is circular (`step.journey.steps → step`), so putting it
17
+ * on ajax/axios options causes "Maximum call stack size exceeded". Class
18
+ * instances are assigned by reference instead of recursed.
19
+ */ export class JourneyStepTag {
20
+ constructor(step){
21
+ _define_property(this, "step", void 0);
22
+ this.step = step;
23
+ }
24
+ }
25
+ /** Unwrap `step.tag().journeyStep` or a JourneyStepTag / StepRecord stamp. */ export function resolveJourneyStep(value) {
26
+ if (value instanceof JourneyStepTag) {
27
+ return value.step;
28
+ }
29
+ if (value && typeof value === 'object') {
30
+ return value;
31
+ }
32
+ return undefined;
33
+ }
34
+
35
+ //# sourceMappingURL=step-tag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/step-tag.ts"],"sourcesContent":["import type { JourneyStepRef, StepRecord } from './types';\n\n/**\n * Non-plain wrapper for {@link StepHandle.tag}.\n *\n * jQuery `ajaxSetup` / axios `mergeConfig` deep-merge plain objects. A raw\n * {@link StepRecord} is circular (`step.journey.steps → step`), so putting it\n * on ajax/axios options causes \"Maximum call stack size exceeded\". Class\n * instances are assigned by reference instead of recursed.\n */\nexport class JourneyStepTag {\n constructor(readonly step: StepRecord) {}\n}\n\n/** Unwrap `step.tag().journeyStep` or a JourneyStepTag / StepRecord stamp. */\nexport function resolveJourneyStep(value: unknown): JourneyStepRef | undefined {\n if (value instanceof JourneyStepTag) {\n return value.step;\n }\n if (value && typeof value === 'object') {\n return value as JourneyStepRef;\n }\n return undefined;\n}\n"],"names":["JourneyStepTag","step","resolveJourneyStep","value","undefined"],"mappings":";;;;;;;;;;;AAEA;;;;;;;CAOC,GACD,OAAO,MAAMA;IACT,YAAY,AAASC,IAAgB,CAAE;;aAAlBA,OAAAA;IAAmB;AAC5C;AAEA,4EAA4E,GAC5E,OAAO,SAASC,mBAAmBC,KAAc;IAC7C,IAAIA,iBAAiBH,gBAAgB;QACjC,OAAOG,MAAMF,IAAI;IACrB;IACA,IAAIE,SAAS,OAAOA,UAAU,UAAU;QACpC,OAAOA;IACX;IACA,OAAOC;AACX"}
@@ -0,0 +1,143 @@
1
+ import type { EndpointPolicy } from '../endpoint-policy';
2
+ /** Arbitrary tag values a developer can attach. */
3
+ export type TagValue = string | number | boolean;
4
+ /** Config for one journey — supplied by the caller (typically `<JourneyScope>` props). */
5
+ export interface JourneyDef {
6
+ /** Journey name — lifecycle key, event field, and concurrency map key. */
7
+ name: string;
8
+ /** Owner team (required tag). */
9
+ team: string;
10
+ /** Functional area, e.g. "sales" (required tag). */
11
+ group: string;
12
+ /** Business component label, e.g. "checkout" — not the Datadog RUM service. */
13
+ service: string;
14
+ /** Whole-journey budget in ms; omit / 0 to disable. */
15
+ timeoutMs?: number;
16
+ /** Default per-step timeout in ms; per-call option overrides. Omit / 0 to disable. */
17
+ stepTimeoutMs?: number;
18
+ /** Slow-request threshold for this journey; falls back to app-wide default. */
19
+ requestTimeoutMs?: number;
20
+ /** Per-endpoint overrides for this journey (longest prefix wins over app-wide). */
21
+ endpoints?: readonly EndpointPolicy[];
22
+ /** Optional expected step names (rides as `expected` on the event). */
23
+ steps?: readonly string[];
24
+ /** Custom tags on every event of this journey. */
25
+ tags?: Record<string, TagValue>;
26
+ }
27
+ export type Outcome = 'good' | 'bad' | 'excluded';
28
+ /** Handle passed to each step function. */
29
+ export interface StepHandle {
30
+ /** Resolved journey name for this step, or null if untraced. */
31
+ readonly name: string | null;
32
+ setAttribute(key: string, value: string | number | boolean): void;
33
+ /** Journey-level tags without closing. */
34
+ setTags(tags: Record<string, TagValue>): void;
35
+ /** Apply optional tags then completeJourney. No-op if name is null. */
36
+ complete(tags?: Record<string, TagValue>): void;
37
+ /** Soft business Bad without throw. No-op if name is null. */
38
+ fail(reason: string): void;
39
+ /**
40
+ * Bind this step to a request explicitly (spread into axios config / fetch init /
41
+ * jQuery ajax settings). Needed when requests fire after an await or while steps
42
+ * overlap — see docs soft ambient. Untraced (noop) steps omit `journeyStep`.
43
+ *
44
+ * The value is a non-plain {@link JourneyStepTag} so jQuery/axios deep-merge will
45
+ * not recurse into the circular StepRecord graph.
46
+ */
47
+ tag(): {
48
+ journeyStep?: unknown;
49
+ };
50
+ }
51
+ /** A journey name, or anything with a `name` (e.g. a JourneyHandle / JourneyDef). */
52
+ export type JourneyNameLike = string | {
53
+ readonly name: string;
54
+ };
55
+ /** First argument to journeyStep / journeyMountStep. */
56
+ export type JourneyStepTarget = JourneyNameLike | null | readonly JourneyNameLike[];
57
+ /** Per-call options for journeyStep. */
58
+ export interface JourneyStepOptions {
59
+ /** Timeout for this step in ms; overrides journey stepTimeoutMs. Omit / 0 to disable. */
60
+ timeoutMs?: number;
61
+ }
62
+ export interface JourneyState {
63
+ name: string;
64
+ team: string;
65
+ group: string;
66
+ service: string;
67
+ verdict: Outcome;
68
+ startedAt: number;
69
+ timeoutMs: number;
70
+ stepTimeoutMs?: number;
71
+ /**
72
+ * Why the journey closed as bad, else null.
73
+ * Engine: request-error | request-latency | journey-timeout | step-timeout | step-error.
74
+ * Also any custom business reason (e.g. call-not-associated).
75
+ */
76
+ reason: string | null;
77
+ steps: StepRecord[];
78
+ expected: string[] | null;
79
+ tags: Record<string, TagValue>;
80
+ requestTimeoutMs?: number;
81
+ endpoints?: readonly EndpointPolicy[];
82
+ timer: ReturnType<typeof setTimeout> | null;
83
+ closed: boolean;
84
+ }
85
+ export interface StepRecord {
86
+ name: string;
87
+ startedAt: number;
88
+ durationMs: number;
89
+ outcome: 'good' | 'bad';
90
+ reason?: string;
91
+ httpStatus?: number;
92
+ attributes: Record<string, string | number | boolean>;
93
+ journey: JourneyState;
94
+ }
95
+ /** Opaque handle to a step, captured at request initiation for response attribution. */
96
+ export type JourneyStepRef = StepRecord;
97
+ export interface JourneyStepEvent {
98
+ name: string;
99
+ /** Offset from the journey start, ms. */
100
+ startMs: number;
101
+ durationMs: number;
102
+ outcome: 'good' | 'bad';
103
+ reason?: string;
104
+ httpStatus?: number;
105
+ attributes?: Record<string, TagValue>;
106
+ }
107
+ /** Neutral journey event payload (journey-specific fields only; session context is host RUM globals). */
108
+ export interface JourneyEvent {
109
+ journey: {
110
+ name: string;
111
+ team: string;
112
+ group: string;
113
+ service: string;
114
+ outcome: Outcome;
115
+ reason?: string;
116
+ durationMs: number;
117
+ steps: JourneyStepEvent[];
118
+ expected?: string[];
119
+ tags?: Record<string, TagValue>;
120
+ };
121
+ }
122
+ /** Destination for a terminal journey event. */
123
+ export type JourneySink = (event: JourneyEvent) => void;
124
+ /** Callable journey — config plus lifecycle helpers bound to `name`. Prefer this over free helpers. */
125
+ export interface JourneyHandle extends JourneyDef {
126
+ /** Open this journey (restarts if already open). */
127
+ start(tags?: Record<string, TagValue>): void;
128
+ /**
129
+ * Record a step. Opens the journey first when it isn't already open
130
+ * (one-shot store actions); no-op start when a `<JourneyScope>` already opened it.
131
+ */
132
+ step<T>(stepName: string, fn: (step: StepHandle) => T | Promise<T>, opts?: JourneyStepOptions): Promise<T>;
133
+ /**
134
+ * Like `step`, deferred one microtask for mount-effect use under `<JourneyScope>`
135
+ * (React runs child effects before parent). Also auto-starts if not already open.
136
+ */
137
+ mountStep<T>(stepName: string, fn: (step: StepHandle) => T | Promise<T>, opts?: JourneyStepOptions): Promise<T>;
138
+ complete(tags?: Record<string, TagValue>): void;
139
+ fail(reason: string): void;
140
+ exclude(): void;
141
+ setTags(tags: Record<string, TagValue>): void;
142
+ }
143
+ //# sourceMappingURL=types.d.ts.map