@servicetitan/journey 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__test-utils__/test-runtime.d.ts.map +1 -0
- package/dist/__test-utils__/test-runtime.js +47 -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/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__/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/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__/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 +8 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +19 -13
- package/dist/config.js.map +1 -1
- package/dist/core/{registry.d.ts → api.d.ts} +2 -22
- package/dist/core/api.d.ts.map +1 -0
- package/dist/core/{registry.js → api.js} +13 -40
- package/dist/core/api.js.map +1 -0
- package/dist/core/endpoint-policy.d.ts.map +1 -0
- package/dist/core/endpoint-policy.js.map +1 -0
- package/dist/core/index.d.ts +4 -4
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/runtime.d.ts +35 -7
- package/dist/core/runtime.d.ts.map +1 -1
- package/dist/core/runtime.js +39 -5
- package/dist/core/runtime.js.map +1 -1
- package/dist/core/step-tag.d.ts +6 -0
- package/dist/core/step-tag.d.ts.map +1 -1
- package/dist/core/step-tag.js +7 -0
- package/dist/core/step-tag.js.map +1 -1
- package/dist/core/types.d.ts +3 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/global.d.ts +67 -0
- package/dist/global.d.ts.map +1 -0
- package/dist/global.js +116 -0
- package/dist/global.js.map +1 -0
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -5
- package/dist/index.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.map +1 -1
- package/dist/integrations/jquery/index.d.ts +2 -2
- package/dist/integrations/jquery/index.d.ts.map +1 -1
- package/dist/integrations/jquery/index.js +3 -2
- package/dist/integrations/jquery/index.js.map +1 -1
- package/dist/integrations/react/scope.d.ts.map +1 -1
- package/dist/integrations/react/scope.js +3 -2
- package/dist/integrations/react/scope.js.map +1 -1
- package/dist/sinks/console.d.ts +11 -0
- package/dist/sinks/console.d.ts.map +1 -0
- package/dist/sinks/console.js +18 -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 +28 -7
- package/dist/sinks/datadog.js.map +1 -1
- package/package.json +2 -2
- package/src/{__mocks__ → __test-utils__}/test-runtime.ts +6 -2
- package/src/__tests__/config.test.ts +9 -12
- package/src/__tests__/{ambient.test.ts → core/ambient.test.ts} +8 -7
- package/src/__tests__/{registry.test.ts → core/api.test.ts} +30 -25
- package/src/__tests__/{define-journey.test.ts → core/define-journey.test.ts} +5 -4
- package/src/__tests__/{requests.test.ts → core/requests.test.ts} +12 -8
- package/src/__tests__/{runtime.test.ts → core/runtime.test.ts} +18 -18
- package/src/__tests__/{step.test.ts → core/step.test.ts} +16 -18
- package/src/__tests__/{timeouts.test.ts → core/timeouts.test.ts} +15 -10
- package/src/__tests__/global.test.ts +113 -0
- package/src/__tests__/{axios.test.ts → integrations/axios.test.ts} +46 -15
- 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} +2 -2
- package/src/__tests__/{fetch.test.ts → integrations/fetch.test.ts} +6 -6
- package/src/__tests__/{jquery.test.ts → integrations/jquery.test.ts} +49 -12
- package/src/__tests__/{request-key.test.ts → integrations/request-key.test.ts} +1 -1
- package/src/__tests__/{scope.test.tsx → integrations/scope.test.tsx} +4 -4
- package/src/__tests__/sinks/console.test.ts +98 -0
- package/src/__tests__/{datadog.test.ts → sinks/datadog.test.ts} +62 -5
- package/src/__tests__/{sinks.test.ts → sinks/sinks.test.ts} +13 -12
- package/src/config.ts +20 -12
- package/src/core/{registry.ts → api.ts} +12 -65
- package/src/core/index.ts +5 -14
- package/src/core/runtime.ts +56 -7
- package/src/core/step-tag.ts +9 -0
- package/src/core/types.ts +3 -1
- package/src/global.ts +196 -0
- package/src/index.ts +20 -7
- package/src/integrations/fetch.ts +2 -2
- package/src/integrations/jquery/index.ts +4 -9
- package/src/integrations/react/scope.tsx +3 -2
- package/src/sinks/console.ts +20 -0
- package/src/sinks/datadog.ts +31 -6
- 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.map +0 -1
- package/dist/core/registry.js.map +0 -1
- package/dist/endpoint-policy.d.ts.map +0 -1
- package/dist/endpoint-policy.js.map +0 -1
- /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
- /package/dist/{endpoint-policy.d.ts → core/endpoint-policy.d.ts} +0 -0
- /package/dist/{endpoint-policy.js → core/endpoint-policy.js} +0 -0
- /package/src/{endpoint-policy.ts → core/endpoint-policy.ts} +0 -0
package/src/config.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { moduleRuntime } from './core/runtime';
|
|
2
|
+
import type { JourneySink } from './core/types';
|
|
3
|
+
import { installJourneyGlobal, registerJourneyRuntime } from './global';
|
|
3
4
|
import {
|
|
4
5
|
sortEndpointsByLongestMatch,
|
|
5
6
|
type AppRequestTimeouts,
|
|
6
7
|
type EndpointPolicy,
|
|
7
|
-
} from './endpoint-policy';
|
|
8
|
+
} from './core/endpoint-policy';
|
|
8
9
|
import { instrumentAxios, type AxiosLikeInstance } from './integrations/axios';
|
|
9
10
|
import { instrumentFetch, type InstrumentFetchOptions } from './integrations/fetch';
|
|
10
11
|
import {
|
|
@@ -13,9 +14,9 @@ import {
|
|
|
13
14
|
type JqueryStatic,
|
|
14
15
|
type WindowWithAppJourney,
|
|
15
16
|
} from './integrations/jquery';
|
|
17
|
+
import { sendToConsole } from './sinks/console';
|
|
16
18
|
import { sendToDatadog } from './sinks/datadog';
|
|
17
19
|
|
|
18
|
-
export { longestEndpointMatch, sortEndpointsByLongestMatch } from './endpoint-policy';
|
|
19
20
|
export type { AppRequestTimeouts, EndpointPolicy };
|
|
20
21
|
|
|
21
22
|
/** Optional app-wide / per-bundle configuration. */
|
|
@@ -25,7 +26,8 @@ export interface JourneyConfig {
|
|
|
25
26
|
/**
|
|
26
27
|
* Replace the sink list used when a journey finishes.
|
|
27
28
|
* Only applied when provided — omit to leave the current list unchanged.
|
|
28
|
-
* Package default is `[sendToDatadog]`; pass `[]` to disable emission.
|
|
29
|
+
* Package default is `[sendToDatadog, sendToConsole]`; pass `[]` to disable emission.
|
|
30
|
+
* `sendToConsole` is a no-op unless `localStorage['st:journey:debug'] === 'true'`.
|
|
29
31
|
*/
|
|
30
32
|
sinks?: readonly JourneySink[];
|
|
31
33
|
/**
|
|
@@ -48,11 +50,13 @@ export interface JourneyConfig {
|
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
/*
|
|
51
|
-
* Default emission
|
|
52
|
-
* (see index.ts `import './config'`) installs Datadog
|
|
53
|
-
* sinks when `sinks` is passed explicitly.
|
|
53
|
+
* Default emission targets + `__stJourney` bag. Kept as a module side effect so any import
|
|
54
|
+
* of this file (see index.ts `import './config'`) installs Datadog, console, and
|
|
55
|
+
* `__stJourney`. configureJourney only replaces sinks when `sinks` is passed explicitly.
|
|
54
56
|
*/
|
|
55
|
-
|
|
57
|
+
moduleRuntime.setSinks([sendToDatadog, sendToConsole]);
|
|
58
|
+
installJourneyGlobal();
|
|
59
|
+
registerJourneyRuntime(moduleRuntime);
|
|
56
60
|
|
|
57
61
|
/**
|
|
58
62
|
* Apply bundle options: request thresholds, sinks, and/or HTTP / legacy wiring.
|
|
@@ -60,13 +64,13 @@ setJourneySinks([sendToDatadog]);
|
|
|
60
64
|
*/
|
|
61
65
|
export function configureJourney(config: JourneyConfig): void {
|
|
62
66
|
if (config.requestTimeouts !== undefined) {
|
|
63
|
-
|
|
67
|
+
moduleRuntime.setRequestTimeouts({
|
|
64
68
|
...config.requestTimeouts,
|
|
65
69
|
endpoints: sortEndpointsByLongestMatch(config.requestTimeouts.endpoints),
|
|
66
70
|
});
|
|
67
71
|
}
|
|
68
72
|
if (config.sinks !== undefined) {
|
|
69
|
-
|
|
73
|
+
moduleRuntime.setSinks(config.sinks);
|
|
70
74
|
}
|
|
71
75
|
if (config.axios !== undefined) {
|
|
72
76
|
instrumentAxios(config.axios);
|
|
@@ -82,6 +86,10 @@ export function configureJourney(config: JourneyConfig): void {
|
|
|
82
86
|
}
|
|
83
87
|
}
|
|
84
88
|
|
|
89
|
+
/**
|
|
90
|
+
* @internal Read the module runtime's app-wide request timeout config.
|
|
91
|
+
* Prefer `configureJourney({ requestTimeouts })` to set thresholds. Test / introspection only.
|
|
92
|
+
*/
|
|
85
93
|
export function getRequestTimeouts(): AppRequestTimeouts {
|
|
86
|
-
return
|
|
94
|
+
return moduleRuntime.getRequestTimeouts();
|
|
87
95
|
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { moduleRuntime } from './runtime';
|
|
2
2
|
import type {
|
|
3
3
|
JourneyDef,
|
|
4
|
-
JourneyEvent,
|
|
5
4
|
JourneyHandle,
|
|
6
|
-
JourneySink,
|
|
7
|
-
JourneyState,
|
|
8
5
|
JourneyStepOptions,
|
|
9
6
|
JourneyStepRef,
|
|
10
7
|
JourneyStepTarget,
|
|
@@ -12,18 +9,6 @@ import type {
|
|
|
12
9
|
TagValue,
|
|
13
10
|
} from './types';
|
|
14
11
|
|
|
15
|
-
export type { JourneyHandle, JourneySink };
|
|
16
|
-
|
|
17
|
-
/** Look up an open journey by name. */
|
|
18
|
-
export function getOpenJourney(name: string): JourneyState | undefined {
|
|
19
|
-
return defaultRuntime.getOpenJourney(name);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** First open journey among `names` (order preserved), or null. */
|
|
23
|
-
export function firstOpenJourney(names: readonly string[]): string | null {
|
|
24
|
-
return defaultRuntime.firstOpenJourney(names);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
12
|
/**
|
|
28
13
|
* Active step for soft-ambient HTTP attribution, or null when 0 or 2+ steps are in flight
|
|
29
14
|
* app-wide. Untagged traffic is attributed to that lone step even if unrelated — prefer
|
|
@@ -31,7 +16,7 @@ export function firstOpenJourney(names: readonly string[]): string | null {
|
|
|
31
16
|
* a request has no explicit stamp.
|
|
32
17
|
*/
|
|
33
18
|
export function activeJourneyStep(): JourneyStepRef | null {
|
|
34
|
-
return
|
|
19
|
+
return moduleRuntime.activeJourneyStep();
|
|
35
20
|
}
|
|
36
21
|
|
|
37
22
|
/**
|
|
@@ -44,7 +29,7 @@ export function reportBackendRequest(
|
|
|
44
29
|
durationMs: number,
|
|
45
30
|
requestKey: string
|
|
46
31
|
): void {
|
|
47
|
-
|
|
32
|
+
moduleRuntime.reportBackendRequest(step, status, durationMs, requestKey);
|
|
48
33
|
}
|
|
49
34
|
|
|
50
35
|
/**
|
|
@@ -58,81 +43,43 @@ export async function journeyStep<T>(
|
|
|
58
43
|
fn: (step: StepHandle) => T | Promise<T>,
|
|
59
44
|
opts?: JourneyStepOptions
|
|
60
45
|
): Promise<T> {
|
|
61
|
-
return
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* journeyStep deferred by one microtask so a parent `<JourneyScope>` opens first
|
|
66
|
-
* (React runs child mount effects before parent). Use for mount-effect steps only.
|
|
67
|
-
*/
|
|
68
|
-
export async function journeyMountStep<T>(
|
|
69
|
-
journey: JourneyStepTarget,
|
|
70
|
-
stepName: string,
|
|
71
|
-
fn: (step: StepHandle) => T | Promise<T>,
|
|
72
|
-
opts?: JourneyStepOptions
|
|
73
|
-
): Promise<T> {
|
|
74
|
-
return defaultRuntime.journeyMountStep(journey, stepName, fn, opts);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/** Replace the sink list (e.g. host bootstrap or tests). Pass `[]` to disable emission. */
|
|
78
|
-
export function setJourneySinks(next: readonly JourneySink[]): void {
|
|
79
|
-
defaultRuntime.setSinks(next);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/** Append a sink alongside the current ones. */
|
|
83
|
-
export function addJourneySink(sink: JourneySink): void {
|
|
84
|
-
defaultRuntime.addSink(sink);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/** Emit a terminal journey event to every configured sink. */
|
|
88
|
-
export function emitJourneyEvent(payload: JourneyEvent): void {
|
|
89
|
-
defaultRuntime.emit(payload);
|
|
46
|
+
return moduleRuntime.journeyStep(journey, stepName, fn, opts);
|
|
90
47
|
}
|
|
91
48
|
|
|
92
49
|
/** Merge custom tags onto an open journey. No-op if not open. Prefer `handle.setTags`. */
|
|
93
50
|
export function setJourneyTags(name: string | null, tags: Record<string, TagValue>): void {
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/** Begin a journey. Restarts (closes) an already-open journey of the same name. */
|
|
98
|
-
export function startJourney(def: JourneyDef): void {
|
|
99
|
-
defaultRuntime.startJourney(def);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/** Fail-fast: record reason and emit immediately. Idempotent via `closed`. */
|
|
103
|
-
export function failJourneyNow(journey: JourneyState, reason: string): void {
|
|
104
|
-
defaultRuntime.failJourneyNow(journey, reason);
|
|
51
|
+
moduleRuntime.setJourneyTags(name, tags);
|
|
105
52
|
}
|
|
106
53
|
|
|
107
54
|
/** Mark a named journey Bad for a custom reason (fail-fast). Prefer `handle.fail`. */
|
|
108
55
|
export function failJourney(name: string, reason: string): void {
|
|
109
|
-
|
|
56
|
+
moduleRuntime.failJourney(name, reason);
|
|
110
57
|
}
|
|
111
58
|
|
|
112
59
|
/** Successful end. Outcome stays Bad if a breach already occurred. No-op when name is null. Prefer `handle.complete`. */
|
|
113
60
|
export function completeJourney(name: string | null, tags?: Record<string, TagValue>): void {
|
|
114
|
-
|
|
61
|
+
moduleRuntime.completeJourney(name, tags);
|
|
115
62
|
}
|
|
116
63
|
|
|
117
64
|
/** User left before finishing. Stays Bad if a breach already occurred (breach-anchored). Prefer `handle.exclude`. */
|
|
118
65
|
export function excludeJourney(name: string): void {
|
|
119
|
-
|
|
66
|
+
moduleRuntime.excludeJourney(name);
|
|
120
67
|
}
|
|
121
68
|
|
|
122
69
|
/** Turn a journey config into a handle with bound lifecycle methods. Preferred public API. */
|
|
123
70
|
export function defineJourney(def: JourneyDef): JourneyHandle {
|
|
124
71
|
const ensureStarted = () => {
|
|
125
|
-
if (!getOpenJourney(def.name)) {
|
|
126
|
-
startJourney(def);
|
|
72
|
+
if (!moduleRuntime.getOpenJourney(def.name)) {
|
|
73
|
+
moduleRuntime.startJourney(def);
|
|
127
74
|
}
|
|
128
75
|
};
|
|
129
76
|
|
|
130
77
|
const handle: JourneyHandle = {
|
|
131
78
|
...def,
|
|
132
79
|
start(tags) {
|
|
133
|
-
startJourney(def);
|
|
80
|
+
moduleRuntime.startJourney(def);
|
|
134
81
|
if (tags) {
|
|
135
|
-
setJourneyTags(def.name, tags);
|
|
82
|
+
moduleRuntime.setJourneyTags(def.name, tags);
|
|
136
83
|
}
|
|
137
84
|
},
|
|
138
85
|
step(stepName, fn, opts) {
|
package/src/core/index.ts
CHANGED
|
@@ -1,34 +1,25 @@
|
|
|
1
|
-
/** Core
|
|
1
|
+
/** Core barrel — public free helpers, handles, and types. Engine lives in `./runtime`. */
|
|
2
2
|
|
|
3
3
|
export {
|
|
4
4
|
activeJourneyStep,
|
|
5
|
-
addJourneySink,
|
|
6
5
|
completeJourney,
|
|
7
6
|
defineJourney,
|
|
8
|
-
emitJourneyEvent,
|
|
9
7
|
excludeJourney,
|
|
10
8
|
failJourney,
|
|
11
|
-
firstOpenJourney,
|
|
12
|
-
journeyMountStep,
|
|
13
9
|
journeyStep,
|
|
14
10
|
reportBackendRequest,
|
|
15
|
-
setJourneySinks,
|
|
16
11
|
setJourneyTags,
|
|
17
|
-
|
|
18
|
-
type JourneyHandle,
|
|
19
|
-
type JourneySink,
|
|
20
|
-
} from './registry';
|
|
12
|
+
} from './api';
|
|
21
13
|
export {
|
|
22
|
-
|
|
23
|
-
defaultRuntime,
|
|
14
|
+
// @internal — accidentally public via package index; not a supported consumer API.
|
|
24
15
|
resetJourney,
|
|
25
|
-
type JourneyRuntime,
|
|
26
|
-
type JourneyRuntimeOptions,
|
|
27
16
|
} from './runtime';
|
|
28
17
|
export type {
|
|
29
18
|
JourneyDef,
|
|
30
19
|
JourneyEvent,
|
|
20
|
+
JourneyHandle,
|
|
31
21
|
JourneyNameLike,
|
|
22
|
+
JourneySink,
|
|
32
23
|
JourneyStepEvent,
|
|
33
24
|
JourneyStepOptions,
|
|
34
25
|
JourneyStepRef,
|
package/src/core/runtime.ts
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
longestEndpointMatch,
|
|
3
3
|
sortEndpointsByLongestMatch,
|
|
4
4
|
type AppRequestTimeouts,
|
|
5
|
-
} from '
|
|
5
|
+
} from './endpoint-policy';
|
|
6
6
|
import { JourneyStepTag } from './step-tag';
|
|
7
7
|
import type {
|
|
8
8
|
JourneyDef,
|
|
@@ -36,16 +36,37 @@ function toName(target: string | { readonly name: string }): string {
|
|
|
36
36
|
|
|
37
37
|
/** @internal Private — not part of the public package API. */
|
|
38
38
|
export interface JourneyRuntimeOptions {
|
|
39
|
-
/** Initial sinks. Defaults to `[]` — package bootstrap installs Datadog on the
|
|
39
|
+
/** Initial sinks. Defaults to `[]` — package bootstrap installs Datadog on the module runtime. */
|
|
40
40
|
sinks?: readonly JourneySink[];
|
|
41
41
|
requestTimeouts?: AppRequestTimeouts;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/** @internal Read-only snapshot for DevTools (`__stJourney.getDebugState`; empty unless debugging). */
|
|
45
|
+
export interface JourneyRuntimeDebugSnapshot {
|
|
46
|
+
openJourneys: readonly {
|
|
47
|
+
name: string;
|
|
48
|
+
team: string;
|
|
49
|
+
group: string;
|
|
50
|
+
service: string;
|
|
51
|
+
verdict: Outcome;
|
|
52
|
+
reason: string | null;
|
|
53
|
+
tags: Record<string, TagValue>;
|
|
54
|
+
stepsInFlight: readonly string[];
|
|
55
|
+
steps: readonly {
|
|
56
|
+
name: string;
|
|
57
|
+
outcome: 'good' | 'bad';
|
|
58
|
+
durationMs: number;
|
|
59
|
+
reason?: string;
|
|
60
|
+
}[];
|
|
61
|
+
}[];
|
|
62
|
+
activeStep: { journey: string; name: string } | null;
|
|
63
|
+
}
|
|
64
|
+
|
|
44
65
|
/**
|
|
45
66
|
* @internal Private — not part of the public package API.
|
|
46
67
|
* Isolated journey engine (open journeys, ambient steps, sinks, request thresholds).
|
|
47
68
|
* App code should keep using free helpers (`defineJourney`, `journeyStep`, …) — they
|
|
48
|
-
* bind to the
|
|
69
|
+
* bind to the module runtime. Prefer `resetJourney()` in tests.
|
|
49
70
|
*/
|
|
50
71
|
export interface JourneyRuntime {
|
|
51
72
|
getRequestTimeouts(): AppRequestTimeouts;
|
|
@@ -56,6 +77,8 @@ export interface JourneyRuntime {
|
|
|
56
77
|
emit(payload: JourneyEvent): void;
|
|
57
78
|
|
|
58
79
|
getOpenJourney(name: string): JourneyState | undefined;
|
|
80
|
+
/** @internal DevTools snapshot of open journeys / ambient step. */
|
|
81
|
+
getDebugSnapshot(): JourneyRuntimeDebugSnapshot;
|
|
59
82
|
firstOpenJourney(names: readonly string[]): string | null;
|
|
60
83
|
setJourneyTags(name: string | null, tags: Record<string, TagValue>): void;
|
|
61
84
|
startJourney(def: JourneyDef): void;
|
|
@@ -278,6 +301,29 @@ export function createJourneyRuntime(options: JourneyRuntimeOptions = {}): Journ
|
|
|
278
301
|
return journeys.get(name);
|
|
279
302
|
},
|
|
280
303
|
|
|
304
|
+
getDebugSnapshot() {
|
|
305
|
+
const active = api.activeJourneyStep();
|
|
306
|
+
return {
|
|
307
|
+
openJourneys: [...journeys.values()].map(journey => ({
|
|
308
|
+
name: journey.name,
|
|
309
|
+
team: journey.team,
|
|
310
|
+
group: journey.group,
|
|
311
|
+
service: journey.service,
|
|
312
|
+
verdict: journey.verdict,
|
|
313
|
+
reason: journey.reason,
|
|
314
|
+
tags: { ...journey.tags },
|
|
315
|
+
stepsInFlight: getInFlightSteps(journey).map(step => step.name),
|
|
316
|
+
steps: journey.steps.map(step => ({
|
|
317
|
+
name: step.name,
|
|
318
|
+
outcome: step.outcome,
|
|
319
|
+
durationMs: step.durationMs,
|
|
320
|
+
...(step.reason ? { reason: step.reason } : {}),
|
|
321
|
+
})),
|
|
322
|
+
})),
|
|
323
|
+
activeStep: active ? { journey: active.journey.name, name: active.name } : null,
|
|
324
|
+
};
|
|
325
|
+
},
|
|
326
|
+
|
|
281
327
|
firstOpenJourney(names) {
|
|
282
328
|
for (const name of names) {
|
|
283
329
|
if (journeys.has(name)) {
|
|
@@ -476,14 +522,17 @@ export function createJourneyRuntime(options: JourneyRuntimeOptions = {}): Journ
|
|
|
476
522
|
|
|
477
523
|
/**
|
|
478
524
|
* @internal Private — not part of the public package API.
|
|
479
|
-
*
|
|
525
|
+
* Module-scoped engine used by free helpers. App code does not need to touch this.
|
|
526
|
+
* Each host/MFE bundle gets its own copy when the package is duplicated.
|
|
480
527
|
*/
|
|
481
|
-
export const
|
|
528
|
+
export const moduleRuntime: JourneyRuntime = createJourneyRuntime();
|
|
482
529
|
|
|
483
530
|
/**
|
|
484
|
-
*
|
|
531
|
+
* @internal Accidentally exported — removing it is a breaking change, so it stays
|
|
532
|
+
* for compatibility. Not part of the supported public API.
|
|
533
|
+
* Reset the module engine (open journeys, ambient steps, sinks, request thresholds).
|
|
485
534
|
* Intended for tests — call in `beforeEach` / `afterEach` instead of manual cleanup.
|
|
486
535
|
*/
|
|
487
536
|
export function resetJourney(options?: JourneyRuntimeOptions): void {
|
|
488
|
-
|
|
537
|
+
moduleRuntime.reset(options);
|
|
489
538
|
}
|
package/src/core/step-tag.ts
CHANGED
|
@@ -10,6 +10,15 @@ import type { JourneyStepRef, StepRecord } from './types';
|
|
|
10
10
|
*/
|
|
11
11
|
export class JourneyStepTag {
|
|
12
12
|
constructor(readonly step: StepRecord) {}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Circular by design (`step.journey.steps → step`) for mergeConfig/deep-merge
|
|
16
|
+
* safety. Prevent accidental `JSON.stringify` (e.g. AxiosError.toJSON() includes
|
|
17
|
+
* config by reference) elsewhere in the app from throwing "circular structure".
|
|
18
|
+
*/
|
|
19
|
+
toJSON(): string {
|
|
20
|
+
return '[JourneyStepTag]';
|
|
21
|
+
}
|
|
13
22
|
}
|
|
14
23
|
|
|
15
24
|
/** Unwrap `step.tag().journeyStep` or a JourneyStepTag / StepRecord stamp. */
|
package/src/core/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EndpointPolicy } from '
|
|
1
|
+
import type { EndpointPolicy } from './endpoint-policy';
|
|
2
2
|
|
|
3
3
|
/** Arbitrary tag values a developer can attach. */
|
|
4
4
|
export type TagValue = string | number | boolean;
|
|
@@ -63,6 +63,7 @@ export interface JourneyStepOptions {
|
|
|
63
63
|
timeoutMs?: number;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
/** @internal Engine open-journey record — not part of the supported public API. */
|
|
66
67
|
export interface JourneyState {
|
|
67
68
|
name: string;
|
|
68
69
|
team: string;
|
|
@@ -87,6 +88,7 @@ export interface JourneyState {
|
|
|
87
88
|
closed: boolean;
|
|
88
89
|
}
|
|
89
90
|
|
|
91
|
+
/** @internal Engine in-flight / completed step record — not part of the supported public API. */
|
|
90
92
|
export interface StepRecord {
|
|
91
93
|
name: string;
|
|
92
94
|
startedAt: number;
|
package/src/global.ts
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import type { JourneyRuntime, JourneyRuntimeDebugSnapshot } from './core/runtime';
|
|
2
|
+
|
|
3
|
+
/** Force-enable journey host wiring outside go* hosts (`__stJourney.enable()`). */
|
|
4
|
+
export const JOURNEY_ENABLED_STORAGE_KEY = 'st:journey:enabled';
|
|
5
|
+
|
|
6
|
+
/** Opt-in journey console logging (`__stJourney.enableDebugging()`). */
|
|
7
|
+
export const JOURNEY_DEBUG_STORAGE_KEY = 'st:journey:debug';
|
|
8
|
+
|
|
9
|
+
/** @internal Shared cross-bundle bag on globalThis (`rum` fallback + DevTools helpers). */
|
|
10
|
+
export const ST_JOURNEY_GLOBAL_KEY = '__stJourney';
|
|
11
|
+
|
|
12
|
+
export interface JourneyDebugRuntimeEntry extends JourneyRuntimeDebugSnapshot {
|
|
13
|
+
id: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface JourneyDebugState {
|
|
17
|
+
/** Force-enable flag (`st:journey:enabled`). */
|
|
18
|
+
enabled: boolean;
|
|
19
|
+
/** Console-logging flag (`st:journey:debug`). */
|
|
20
|
+
debug: boolean;
|
|
21
|
+
hasRum: boolean;
|
|
22
|
+
runtimes: JourneyDebugRuntimeEntry[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @internal Cross-bundle surface on `globalThis.__stJourney`.
|
|
27
|
+
* Prefer importing `isJourneyEnabled` / `isJourneyDebugEnabled` from the package in app code.
|
|
28
|
+
*/
|
|
29
|
+
export interface StJourneyGlobal {
|
|
30
|
+
/**
|
|
31
|
+
* Fallback RUM instance from `setJourneyRum` when `globalThis.DD_RUM` is absent.
|
|
32
|
+
* Prefer `DD_RUM` (ST host: `datadogGuard`).
|
|
33
|
+
*/
|
|
34
|
+
rum?: unknown;
|
|
35
|
+
enable(): void;
|
|
36
|
+
disable(): void;
|
|
37
|
+
isEnabled(): boolean;
|
|
38
|
+
enableDebugging(): void;
|
|
39
|
+
disableDebugging(): void;
|
|
40
|
+
isDebugEnabled(): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Open-journey snapshot for DevTools. Returns empty `runtimes` unless debugging
|
|
43
|
+
* is enabled (`enableDebugging()` / `st:journey:debug`).
|
|
44
|
+
*/
|
|
45
|
+
getDebugState(): JourneyDebugState;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface RuntimeRegistration {
|
|
49
|
+
id: string;
|
|
50
|
+
getSnapshot: () => JourneyRuntimeDebugSnapshot;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface StJourneyGlobalInternal extends StJourneyGlobal {
|
|
54
|
+
/** @internal */
|
|
55
|
+
runtimeRegistrations: RuntimeRegistration[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let runtimeSeq = 0;
|
|
59
|
+
|
|
60
|
+
function readFlag(key: string): boolean {
|
|
61
|
+
try {
|
|
62
|
+
return globalThis.localStorage?.getItem(key) === 'true';
|
|
63
|
+
} catch {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function writeFlag(key: string, on: boolean): void {
|
|
69
|
+
try {
|
|
70
|
+
if (on) {
|
|
71
|
+
globalThis.localStorage?.setItem(key, 'true');
|
|
72
|
+
} else {
|
|
73
|
+
globalThis.localStorage?.removeItem(key);
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
// ignore storage failures (privacy mode, non-browser)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** True when `localStorage['st:journey:enabled'] === 'true'` (force-on outside go*). */
|
|
81
|
+
export function isJourneyEnabled(): boolean {
|
|
82
|
+
return readFlag(JOURNEY_ENABLED_STORAGE_KEY);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Set the force-enable flag. Reload after calling from DevTools. */
|
|
86
|
+
export function enableJourney(): void {
|
|
87
|
+
writeFlag(JOURNEY_ENABLED_STORAGE_KEY, true);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Clear the force-enable flag. Reload after calling from DevTools. */
|
|
91
|
+
export function disableJourney(): void {
|
|
92
|
+
writeFlag(JOURNEY_ENABLED_STORAGE_KEY, false);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** True when `localStorage['st:journey:debug'] === 'true'`. */
|
|
96
|
+
export function isJourneyDebugEnabled(): boolean {
|
|
97
|
+
return readFlag(JOURNEY_DEBUG_STORAGE_KEY);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Set the console-debug flag. Reload after calling from DevTools. */
|
|
101
|
+
export function enableJourneyDebugging(): void {
|
|
102
|
+
writeFlag(JOURNEY_DEBUG_STORAGE_KEY, true);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Clear the console-debug flag. Reload after calling from DevTools. */
|
|
106
|
+
export function disableJourneyDebugging(): void {
|
|
107
|
+
writeFlag(JOURNEY_DEBUG_STORAGE_KEY, false);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function hasRum(): boolean {
|
|
111
|
+
return (
|
|
112
|
+
(globalThis as Record<string, unknown>).DD_RUM != null || getJourneyRumFallback() != null
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function buildDebugState(runtimes: RuntimeRegistration[]): JourneyDebugState {
|
|
117
|
+
if (!isJourneyDebugEnabled()) {
|
|
118
|
+
return {
|
|
119
|
+
enabled: isJourneyEnabled(),
|
|
120
|
+
debug: false,
|
|
121
|
+
hasRum: hasRum(),
|
|
122
|
+
runtimes: [],
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
enabled: isJourneyEnabled(),
|
|
128
|
+
debug: true,
|
|
129
|
+
hasRum: hasRum(),
|
|
130
|
+
runtimes: runtimes.map(entry => ({
|
|
131
|
+
id: entry.id,
|
|
132
|
+
...entry.getSnapshot(),
|
|
133
|
+
})),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function ensureGlobal(): StJourneyGlobalInternal {
|
|
138
|
+
const g = globalThis as Record<string, unknown>;
|
|
139
|
+
const existing = g[ST_JOURNEY_GLOBAL_KEY] as StJourneyGlobalInternal | undefined;
|
|
140
|
+
// Reuse when a prior load already installed the full surface (incl. enable).
|
|
141
|
+
if (existing?.runtimeRegistrations && typeof existing.enable === 'function') {
|
|
142
|
+
return existing;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const runtimes: RuntimeRegistration[] = existing?.runtimeRegistrations ?? [];
|
|
146
|
+
|
|
147
|
+
const api: StJourneyGlobalInternal = {
|
|
148
|
+
rum: existing?.rum,
|
|
149
|
+
runtimeRegistrations: runtimes,
|
|
150
|
+
enable: enableJourney,
|
|
151
|
+
disable: disableJourney,
|
|
152
|
+
isEnabled: isJourneyEnabled,
|
|
153
|
+
enableDebugging: enableJourneyDebugging,
|
|
154
|
+
disableDebugging: disableJourneyDebugging,
|
|
155
|
+
isDebugEnabled: isJourneyDebugEnabled,
|
|
156
|
+
getDebugState() {
|
|
157
|
+
return buildDebugState(api.runtimeRegistrations);
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
g[ST_JOURNEY_GLOBAL_KEY] = api;
|
|
162
|
+
return api;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* @internal Register this bundle's module runtime on `__stJourney` so DevTools can
|
|
167
|
+
* list every host/MFE engine copy on the page.
|
|
168
|
+
*/
|
|
169
|
+
export function registerJourneyRuntime(runtime: JourneyRuntime): void {
|
|
170
|
+
const globalApi = ensureGlobal();
|
|
171
|
+
const id = `runtime-${++runtimeSeq}`;
|
|
172
|
+
globalApi.runtimeRegistrations.push({
|
|
173
|
+
id,
|
|
174
|
+
getSnapshot: () => runtime.getDebugSnapshot(),
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** @internal Ensure `__stJourney` exists (idempotent). */
|
|
179
|
+
export function installJourneyGlobal(): void {
|
|
180
|
+
ensureGlobal();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* @internal Store a fallback RUM instance on `__stJourney.rum` when `DD_RUM` is absent.
|
|
185
|
+
* Used by `setJourneyRum`.
|
|
186
|
+
*/
|
|
187
|
+
export function setJourneyRumFallback(rum: unknown): void {
|
|
188
|
+
ensureGlobal().rum = rum;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** @internal Read `__stJourney.rum` (setJourneyRum fallback). */
|
|
192
|
+
export function getJourneyRumFallback(): unknown {
|
|
193
|
+
const existing = (globalThis as Record<string, unknown>)[ST_JOURNEY_GLOBAL_KEY] as
|
|
194
|
+
StJourneyGlobal | undefined;
|
|
195
|
+
return existing?.rum;
|
|
196
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -6,17 +6,18 @@
|
|
|
6
6
|
import './config';
|
|
7
7
|
|
|
8
8
|
export {
|
|
9
|
-
//
|
|
10
|
-
activeJourneyStep,
|
|
11
|
-
// Prefer defineJourney(...).complete/fail/… — free helpers for name-from-context call sites.
|
|
12
|
-
completeJourney,
|
|
9
|
+
// Preferred app API.
|
|
13
10
|
defineJourney,
|
|
11
|
+
// Name-string lifecycle (prefer handle methods when you own the journey).
|
|
12
|
+
completeJourney,
|
|
14
13
|
excludeJourney,
|
|
15
14
|
failJourney,
|
|
16
15
|
journeyStep,
|
|
16
|
+
setJourneyTags,
|
|
17
|
+
// Custom transport adapters / request attribution.
|
|
18
|
+
activeJourneyStep,
|
|
17
19
|
reportBackendRequest,
|
|
18
20
|
resolveJourneyStep,
|
|
19
|
-
setJourneyTags,
|
|
20
21
|
JourneyStepTag,
|
|
21
22
|
type JourneyDef,
|
|
22
23
|
type JourneyHandle,
|
|
@@ -36,14 +37,26 @@ export {
|
|
|
36
37
|
|
|
37
38
|
export type { JourneySink } from './core';
|
|
38
39
|
|
|
39
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* @internal Accidentally exported — removing it is a breaking change, so it stays
|
|
42
|
+
* for compatibility. Not part of the supported public API. Test helper only.
|
|
43
|
+
*/
|
|
40
44
|
export { resetJourney } from './core';
|
|
41
45
|
|
|
42
46
|
export { instrumentAxios } from './integrations/axios';
|
|
43
47
|
export { instrumentFetch } from './integrations/fetch';
|
|
44
48
|
export { exposeAppJourney, instrumentJquery } from './integrations/jquery';
|
|
45
49
|
export { requestKey, type JourneyTimedMeta } from './integrations/request-key';
|
|
46
|
-
export {
|
|
50
|
+
export {
|
|
51
|
+
disableJourney,
|
|
52
|
+
disableJourneyDebugging,
|
|
53
|
+
enableJourney,
|
|
54
|
+
enableJourneyDebugging,
|
|
55
|
+
isJourneyDebugEnabled,
|
|
56
|
+
isJourneyEnabled,
|
|
57
|
+
} from './global';
|
|
58
|
+
export { sendToConsole } from './sinks/console';
|
|
59
|
+
export { normalizeTagValue, sendToDatadog, setJourneyRum } from './sinks/datadog';
|
|
47
60
|
|
|
48
61
|
export type { JourneyEvent, JourneyStepEvent, TagValue } from './core';
|
|
49
62
|
|
|
@@ -2,10 +2,10 @@ import { activeJourneyStep, reportBackendRequest, resolveJourneyStep } from '../
|
|
|
2
2
|
import { requestKey, type JourneyTimedMeta } from './request-key';
|
|
3
3
|
|
|
4
4
|
/** Minimal structural fetch shape. */
|
|
5
|
-
|
|
5
|
+
type FetchLike = (input: any, init?: any) => Promise<{ status: number }>;
|
|
6
6
|
|
|
7
7
|
/** Holder of a `fetch` property — globalThis by default, or a scoped object. */
|
|
8
|
-
|
|
8
|
+
interface FetchTarget {
|
|
9
9
|
fetch: FetchLike;
|
|
10
10
|
}
|
|
11
11
|
|