@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.
- package/README.md +7 -0
- package/dist/__mocks__/test-runtime.d.ts +16 -0
- package/dist/__mocks__/test-runtime.d.ts.map +1 -0
- package/dist/__tests__/ambient.test.d.ts +2 -0
- package/dist/__tests__/ambient.test.d.ts.map +1 -0
- package/dist/__tests__/axios.test.d.ts +2 -0
- package/dist/__tests__/axios.test.d.ts.map +1 -0
- package/dist/__tests__/config.test.d.ts +2 -0
- package/dist/__tests__/config.test.d.ts.map +1 -0
- package/dist/__tests__/context.test.d.ts +2 -0
- package/dist/__tests__/context.test.d.ts.map +1 -0
- package/dist/__tests__/datadog.test.d.ts +2 -0
- package/dist/__tests__/datadog.test.d.ts.map +1 -0
- package/dist/__tests__/define-journey.test.d.ts +2 -0
- package/dist/__tests__/define-journey.test.d.ts.map +1 -0
- package/dist/__tests__/expose-app-journey.test.d.ts +2 -0
- package/dist/__tests__/expose-app-journey.test.d.ts.map +1 -0
- package/dist/__tests__/fetch.test.d.ts +2 -0
- package/dist/__tests__/fetch.test.d.ts.map +1 -0
- package/dist/__tests__/jquery.test.d.ts +2 -0
- package/dist/__tests__/jquery.test.d.ts.map +1 -0
- package/dist/__tests__/registry.test.d.ts +2 -0
- package/dist/__tests__/registry.test.d.ts.map +1 -0
- package/dist/__tests__/request-key.test.d.ts +2 -0
- package/dist/__tests__/request-key.test.d.ts.map +1 -0
- package/dist/__tests__/requests.test.d.ts +2 -0
- package/dist/__tests__/requests.test.d.ts.map +1 -0
- package/dist/__tests__/runtime.test.d.ts +2 -0
- package/dist/__tests__/runtime.test.d.ts.map +1 -0
- package/dist/__tests__/scope.test.d.ts +2 -0
- package/dist/__tests__/scope.test.d.ts.map +1 -0
- package/dist/__tests__/sinks.test.d.ts +2 -0
- package/dist/__tests__/sinks.test.d.ts.map +1 -0
- package/dist/__tests__/step.test.d.ts +2 -0
- package/dist/__tests__/step.test.d.ts.map +1 -0
- package/dist/__tests__/timeouts.test.d.ts +2 -0
- package/dist/__tests__/timeouts.test.d.ts.map +1 -0
- package/dist/config.d.ts +42 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +46 -0
- package/dist/config.js.map +1 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +5 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/registry.d.ts +50 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/core/registry.js +102 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/runtime.d.ts +51 -0
- package/dist/core/runtime.d.ts.map +1 -0
- package/dist/core/runtime.js +401 -0
- package/dist/core/runtime.js.map +1 -0
- package/dist/core/step-tag.d.ts +16 -0
- package/dist/core/step-tag.d.ts.map +1 -0
- package/dist/core/step-tag.js +35 -0
- package/dist/core/step-tag.js.map +1 -0
- package/dist/core/types.d.ts +143 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +3 -0
- package/dist/core/types.js.map +1 -0
- package/dist/endpoint-policy.d.ts +29 -0
- package/dist/endpoint-policy.d.ts.map +1 -0
- package/dist/endpoint-policy.js +39 -0
- package/dist/endpoint-policy.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/axios.d.ts +23 -0
- package/dist/integrations/axios.d.ts.map +1 -0
- package/dist/integrations/axios.js +51 -0
- package/dist/integrations/axios.js.map +1 -0
- package/dist/integrations/fetch.d.ts +21 -0
- package/dist/integrations/fetch.d.ts.map +1 -0
- package/dist/integrations/fetch.js +63 -0
- package/dist/integrations/fetch.js.map +1 -0
- package/dist/integrations/jquery/index.d.ts +22 -0
- package/dist/integrations/jquery/index.d.ts.map +1 -0
- package/dist/integrations/jquery/index.js +27 -0
- package/dist/integrations/jquery/index.js.map +1 -0
- package/dist/integrations/jquery/instrument.d.ts +18 -0
- package/dist/integrations/jquery/instrument.d.ts.map +1 -0
- package/dist/integrations/jquery/instrument.js +44 -0
- package/dist/integrations/jquery/instrument.js.map +1 -0
- package/dist/integrations/react/context.d.ts +6 -0
- package/dist/integrations/react/context.d.ts.map +1 -0
- package/dist/integrations/react/context.js +14 -0
- package/dist/integrations/react/context.js.map +1 -0
- package/dist/integrations/react/scope.d.ts +13 -0
- package/dist/integrations/react/scope.d.ts.map +1 -0
- package/dist/integrations/react/scope.js +27 -0
- package/dist/integrations/react/scope.js.map +1 -0
- package/dist/integrations/request-key.d.ts +17 -0
- package/dist/integrations/request-key.d.ts.map +1 -0
- package/dist/integrations/request-key.js +39 -0
- package/dist/integrations/request-key.js.map +1 -0
- package/dist/sinks/datadog.d.ts +6 -0
- package/dist/sinks/datadog.d.ts.map +1 -0
- package/dist/sinks/datadog.js +64 -0
- package/dist/sinks/datadog.js.map +1 -0
- package/package.json +44 -0
- package/src/__mocks__/test-runtime.ts +37 -0
- package/src/__tests__/ambient.test.ts +101 -0
- package/src/__tests__/axios.test.ts +365 -0
- package/src/__tests__/config.test.ts +261 -0
- package/src/__tests__/context.test.tsx +84 -0
- package/src/__tests__/datadog.test.ts +160 -0
- package/src/__tests__/define-journey.test.ts +176 -0
- package/src/__tests__/expose-app-journey.test.ts +149 -0
- package/src/__tests__/fetch.test.ts +264 -0
- package/src/__tests__/jquery.test.ts +327 -0
- package/src/__tests__/registry.test.ts +265 -0
- package/src/__tests__/request-key.test.ts +81 -0
- package/src/__tests__/requests.test.ts +337 -0
- package/src/__tests__/runtime.test.ts +273 -0
- package/src/__tests__/scope.test.tsx +128 -0
- package/src/__tests__/sinks.test.ts +99 -0
- package/src/__tests__/step.test.ts +305 -0
- package/src/__tests__/timeouts.test.ts +226 -0
- package/src/config.ts +87 -0
- package/src/core/index.ts +39 -0
- package/src/core/registry.ts +162 -0
- package/src/core/runtime.ts +489 -0
- package/src/core/step-tag.ts +24 -0
- package/src/core/types.ts +160 -0
- package/src/endpoint-policy.ts +66 -0
- package/src/index.ts +51 -0
- package/src/integrations/axios.ts +85 -0
- package/src/integrations/fetch.ts +97 -0
- package/src/integrations/jquery/index.ts +62 -0
- package/src/integrations/jquery/instrument.ts +84 -0
- package/src/integrations/react/context.ts +17 -0
- package/src/integrations/react/scope.tsx +32 -0
- package/src/integrations/request-key.ts +46 -0
- package/src/sinks/datadog.ts +71 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timing metadata stamped onto a request config so the response handler can attribute
|
|
3
|
+
* and measure the round trip. Intersect with your transport's config type.
|
|
4
|
+
*
|
|
5
|
+
* `journeyStep` is a JourneyStepTag (from `step.tag()` or ambient instrumentation)
|
|
6
|
+
* or, historically, a raw StepRecord. Adapters unwrap via `resolveJourneyStep`.
|
|
7
|
+
*/
|
|
8
|
+
export interface JourneyTimedMeta {
|
|
9
|
+
journeyStart?: number;
|
|
10
|
+
journeyStep?: unknown;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Endpoint key for timeout / ignore lookup: PATH for same-origin (vs instance baseURL),
|
|
14
|
+
* or "host/path" for cross-domain.
|
|
15
|
+
*/
|
|
16
|
+
export declare function requestKey(url: string | undefined, configBaseURL: string | undefined, instanceBaseURL: string | undefined): string;
|
|
17
|
+
//# sourceMappingURL=request-key.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-key.d.ts","sourceRoot":"","sources":["../../src/integrations/request-key.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;AAUD;;;GAGG;AACH,wBAAgB,UAAU,CACtB,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,eAAe,EAAE,MAAM,GAAG,SAAS,GACpC,MAAM,CAiBR"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timing metadata stamped onto a request config so the response handler can attribute
|
|
3
|
+
* and measure the round trip. Intersect with your transport's config type.
|
|
4
|
+
*
|
|
5
|
+
* `journeyStep` is a JourneyStepTag (from `step.tag()` or ambient instrumentation)
|
|
6
|
+
* or, historically, a raw StepRecord. Adapters unwrap via `resolveJourneyStep`.
|
|
7
|
+
*/ function pageOrigin() {
|
|
8
|
+
try {
|
|
9
|
+
var _ref;
|
|
10
|
+
var _globalThis_location;
|
|
11
|
+
return (_ref = (_globalThis_location = globalThis.location) === null || _globalThis_location === void 0 ? void 0 : _globalThis_location.origin) !== null && _ref !== void 0 ? _ref : '';
|
|
12
|
+
} catch (unused) {
|
|
13
|
+
return '';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Endpoint key for timeout / ignore lookup: PATH for same-origin (vs instance baseURL),
|
|
18
|
+
* or "host/path" for cross-domain.
|
|
19
|
+
*/ export function requestKey(url, configBaseURL, instanceBaseURL) {
|
|
20
|
+
var _ref;
|
|
21
|
+
const origin = pageOrigin();
|
|
22
|
+
const base = (_ref = configBaseURL !== null && configBaseURL !== void 0 ? configBaseURL : instanceBaseURL) !== null && _ref !== void 0 ? _ref : origin;
|
|
23
|
+
let abs;
|
|
24
|
+
try {
|
|
25
|
+
abs = new URL(url !== null && url !== void 0 ? url : '', base || undefined);
|
|
26
|
+
} catch (unused) {
|
|
27
|
+
return url !== null && url !== void 0 ? url : '';
|
|
28
|
+
}
|
|
29
|
+
let homeOrigin;
|
|
30
|
+
try {
|
|
31
|
+
const homeBase = instanceBaseURL !== null && instanceBaseURL !== void 0 ? instanceBaseURL : origin;
|
|
32
|
+
homeOrigin = new URL(homeBase || 'http://localhost').origin;
|
|
33
|
+
} catch (unused) {
|
|
34
|
+
homeOrigin = origin;
|
|
35
|
+
}
|
|
36
|
+
return abs.origin === homeOrigin ? abs.pathname : abs.host + abs.pathname;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
//# sourceMappingURL=request-key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/integrations/request-key.ts"],"sourcesContent":["/**\n * Timing metadata stamped onto a request config so the response handler can attribute\n * and measure the round trip. Intersect with your transport's config type.\n *\n * `journeyStep` is a JourneyStepTag (from `step.tag()` or ambient instrumentation)\n * or, historically, a raw StepRecord. Adapters unwrap via `resolveJourneyStep`.\n */\nexport interface JourneyTimedMeta {\n journeyStart?: number;\n journeyStep?: unknown;\n}\n\nfunction pageOrigin(): string {\n try {\n return globalThis.location?.origin ?? '';\n } catch {\n return '';\n }\n}\n\n/**\n * Endpoint key for timeout / ignore lookup: PATH for same-origin (vs instance baseURL),\n * or \"host/path\" for cross-domain.\n */\nexport function requestKey(\n url: string | undefined,\n configBaseURL: string | undefined,\n instanceBaseURL: string | undefined\n): string {\n const origin = pageOrigin();\n const base = configBaseURL ?? instanceBaseURL ?? origin;\n let abs: URL;\n try {\n abs = new URL(url ?? '', base || undefined);\n } catch {\n return url ?? '';\n }\n let homeOrigin: string;\n try {\n const homeBase = instanceBaseURL ?? origin;\n homeOrigin = new URL(homeBase || 'http://localhost').origin;\n } catch {\n homeOrigin = origin;\n }\n return abs.origin === homeOrigin ? abs.pathname : abs.host + abs.pathname;\n}\n"],"names":["pageOrigin","globalThis","location","origin","requestKey","url","configBaseURL","instanceBaseURL","base","abs","URL","undefined","homeOrigin","homeBase","pathname","host"],"mappings":"AAAA;;;;;;CAMC,GAMD,SAASA;IACL,IAAI;;YACOC;QAAP,gBAAOA,uBAAAA,WAAWC,QAAQ,cAAnBD,2CAAAA,qBAAqBE,MAAM,uCAAI;IAC1C,EAAE,eAAM;QACJ,OAAO;IACX;AACJ;AAEA;;;CAGC,GACD,OAAO,SAASC,WACZC,GAAuB,EACvBC,aAAiC,EACjCC,eAAmC;QAGtBD;IADb,MAAMH,SAASH;IACf,MAAMQ,QAAOF,OAAAA,0BAAAA,2BAAAA,gBAAiBC,6BAAjBD,kBAAAA,OAAoCH;IACjD,IAAIM;IACJ,IAAI;QACAA,MAAM,IAAIC,IAAIL,gBAAAA,iBAAAA,MAAO,IAAIG,QAAQG;IACrC,EAAE,eAAM;QACJ,OAAON,gBAAAA,iBAAAA,MAAO;IAClB;IACA,IAAIO;IACJ,IAAI;QACA,MAAMC,WAAWN,4BAAAA,6BAAAA,kBAAmBJ;QACpCS,aAAa,IAAIF,IAAIG,YAAY,oBAAoBV,MAAM;IAC/D,EAAE,eAAM;QACJS,aAAaT;IACjB;IACA,OAAOM,IAAIN,MAAM,KAAKS,aAAaH,IAAIK,QAAQ,GAAGL,IAAIM,IAAI,GAAGN,IAAIK,QAAQ;AAC7E"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { JourneyEvent } from '../core/types';
|
|
2
|
+
/** Host-only: publish the initialized RUM instance so MFE bundles share the same session. */
|
|
3
|
+
export declare function setJourneyRum(rum: unknown): void;
|
|
4
|
+
/** Ship one terminal journey as a RUM custom action (`user_journey`). */
|
|
5
|
+
export declare function sendToDatadog(payload: JourneyEvent): void;
|
|
6
|
+
//# sourceMappingURL=datadog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"datadog.d.ts","sourceRoot":"","sources":["../../src/sinks/datadog.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAA8B,MAAM,eAAe,CAAC;AAM9E,6FAA6F;AAC7F,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAEhD;AAwCD,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAkBzD"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { datadogRum } from '@datadog/browser-rum';
|
|
2
|
+
const JOURNEY_ACTION = 'user_journey';
|
|
3
|
+
const RUM_GLOBAL_KEY = '__stJourneyRum';
|
|
4
|
+
/** Host-only: publish the initialized RUM instance so MFE bundles share the same session. */ export function setJourneyRum(rum) {
|
|
5
|
+
globalThis[RUM_GLOBAL_KEY] = rum;
|
|
6
|
+
}
|
|
7
|
+
function resolveRum() {
|
|
8
|
+
const shared = globalThis[RUM_GLOBAL_KEY];
|
|
9
|
+
return shared !== null && shared !== void 0 ? shared : datadogRum;
|
|
10
|
+
}
|
|
11
|
+
// Match Datadog metric-tag value rules so RUM attributes join derived SLO tags.
|
|
12
|
+
const MAX_TAG_VALUE_LEN = 200;
|
|
13
|
+
function normalizeTagValue(value) {
|
|
14
|
+
return value.toLowerCase().replace(/[^a-z0-9_\-:./]/g, '_').replace(/_+/g, '_').slice(0, MAX_TAG_VALUE_LEN).replace(/_+$/, '');
|
|
15
|
+
}
|
|
16
|
+
function normalizeTags(tags) {
|
|
17
|
+
const out = {};
|
|
18
|
+
for (const [key, value] of Object.entries(tags)){
|
|
19
|
+
out[key] = typeof value === 'string' ? normalizeTagValue(value) : value;
|
|
20
|
+
}
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
function toDatadogStep(step) {
|
|
24
|
+
/* eslint-disable @typescript-eslint/naming-convention -- Datadog RUM wire format (snake_case) */ return {
|
|
25
|
+
name: step.name,
|
|
26
|
+
start_ms: step.startMs,
|
|
27
|
+
duration_ms: step.durationMs,
|
|
28
|
+
outcome: step.outcome,
|
|
29
|
+
...step.reason ? {
|
|
30
|
+
reason: step.reason
|
|
31
|
+
} : {},
|
|
32
|
+
...step.httpStatus !== undefined ? {
|
|
33
|
+
http_status: step.httpStatus
|
|
34
|
+
} : {},
|
|
35
|
+
...step.attributes ? {
|
|
36
|
+
attributes: step.attributes
|
|
37
|
+
} : {}
|
|
38
|
+
};
|
|
39
|
+
/* eslint-enable @typescript-eslint/naming-convention */ }
|
|
40
|
+
/** Ship one terminal journey as a RUM custom action (`user_journey`). */ export function sendToDatadog(payload) {
|
|
41
|
+
const j = payload.journey;
|
|
42
|
+
/* eslint-disable @typescript-eslint/naming-convention -- Datadog RUM wire format (snake_case) */ resolveRum().addAction(JOURNEY_ACTION, {
|
|
43
|
+
journey: {
|
|
44
|
+
name: normalizeTagValue(j.name),
|
|
45
|
+
team: normalizeTagValue(j.team),
|
|
46
|
+
group: normalizeTagValue(j.group),
|
|
47
|
+
service: normalizeTagValue(j.service),
|
|
48
|
+
outcome: j.outcome,
|
|
49
|
+
...j.reason ? {
|
|
50
|
+
reason: normalizeTagValue(j.reason)
|
|
51
|
+
} : {},
|
|
52
|
+
duration_ms: j.durationMs,
|
|
53
|
+
steps: j.steps.map(toDatadogStep),
|
|
54
|
+
...j.expected ? {
|
|
55
|
+
expected: j.expected
|
|
56
|
+
} : {},
|
|
57
|
+
...j.tags ? {
|
|
58
|
+
tags: normalizeTags(j.tags)
|
|
59
|
+
} : {}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
/* eslint-enable @typescript-eslint/naming-convention */ }
|
|
63
|
+
|
|
64
|
+
//# sourceMappingURL=datadog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/sinks/datadog.ts"],"sourcesContent":["import { datadogRum } from '@datadog/browser-rum';\n\nimport type { JourneyEvent, JourneyStepEvent, TagValue } from '../core/types';\n\nconst JOURNEY_ACTION = 'user_journey';\n\nconst RUM_GLOBAL_KEY = '__stJourneyRum';\n\n/** Host-only: publish the initialized RUM instance so MFE bundles share the same session. */\nexport function setJourneyRum(rum: unknown): void {\n (globalThis as Record<string, unknown>)[RUM_GLOBAL_KEY] = rum;\n}\n\nfunction resolveRum(): typeof datadogRum {\n const shared = (globalThis as Record<string, unknown>)[RUM_GLOBAL_KEY];\n return (shared as typeof datadogRum | undefined) ?? datadogRum;\n}\n\n// Match Datadog metric-tag value rules so RUM attributes join derived SLO tags.\nconst MAX_TAG_VALUE_LEN = 200;\nfunction normalizeTagValue(value: string): string {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9_\\-:./]/g, '_')\n .replace(/_+/g, '_')\n .slice(0, MAX_TAG_VALUE_LEN)\n .replace(/_+$/, '');\n}\n\nfunction normalizeTags(tags: Record<string, TagValue>): Record<string, TagValue> {\n const out: Record<string, TagValue> = {};\n for (const [key, value] of Object.entries(tags)) {\n out[key] = typeof value === 'string' ? normalizeTagValue(value) : value;\n }\n return out;\n}\n\nfunction toDatadogStep(step: JourneyStepEvent) {\n /* eslint-disable @typescript-eslint/naming-convention -- Datadog RUM wire format (snake_case) */\n return {\n name: step.name,\n start_ms: step.startMs,\n duration_ms: step.durationMs,\n outcome: step.outcome,\n ...(step.reason ? { reason: step.reason } : {}),\n ...(step.httpStatus !== undefined ? { http_status: step.httpStatus } : {}),\n ...(step.attributes ? { attributes: step.attributes } : {}),\n };\n /* eslint-enable @typescript-eslint/naming-convention */\n}\n\n/** Ship one terminal journey as a RUM custom action (`user_journey`). */\nexport function sendToDatadog(payload: JourneyEvent): void {\n const j = payload.journey;\n /* eslint-disable @typescript-eslint/naming-convention -- Datadog RUM wire format (snake_case) */\n resolveRum().addAction(JOURNEY_ACTION, {\n journey: {\n name: normalizeTagValue(j.name),\n team: normalizeTagValue(j.team),\n group: normalizeTagValue(j.group),\n service: normalizeTagValue(j.service),\n outcome: j.outcome,\n ...(j.reason ? { reason: normalizeTagValue(j.reason) } : {}),\n duration_ms: j.durationMs,\n steps: j.steps.map(toDatadogStep),\n ...(j.expected ? { expected: j.expected } : {}),\n ...(j.tags ? { tags: normalizeTags(j.tags) } : {}),\n },\n });\n /* eslint-enable @typescript-eslint/naming-convention */\n}\n"],"names":["datadogRum","JOURNEY_ACTION","RUM_GLOBAL_KEY","setJourneyRum","rum","globalThis","resolveRum","shared","MAX_TAG_VALUE_LEN","normalizeTagValue","value","toLowerCase","replace","slice","normalizeTags","tags","out","key","Object","entries","toDatadogStep","step","name","start_ms","startMs","duration_ms","durationMs","outcome","reason","httpStatus","undefined","http_status","attributes","sendToDatadog","payload","j","journey","addAction","team","group","service","steps","map","expected"],"mappings":"AAAA,SAASA,UAAU,QAAQ,uBAAuB;AAIlD,MAAMC,iBAAiB;AAEvB,MAAMC,iBAAiB;AAEvB,2FAA2F,GAC3F,OAAO,SAASC,cAAcC,GAAY;IACrCC,UAAsC,CAACH,eAAe,GAAGE;AAC9D;AAEA,SAASE;IACL,MAAMC,SAAS,AAACF,UAAsC,CAACH,eAAe;IACtE,OAAQK,mBAAAA,oBAAAA,SAA4CP;AACxD;AAEA,gFAAgF;AAChF,MAAMQ,oBAAoB;AAC1B,SAASC,kBAAkBC,KAAa;IACpC,OAAOA,MACFC,WAAW,GACXC,OAAO,CAAC,oBAAoB,KAC5BA,OAAO,CAAC,OAAO,KACfC,KAAK,CAAC,GAAGL,mBACTI,OAAO,CAAC,OAAO;AACxB;AAEA,SAASE,cAAcC,IAA8B;IACjD,MAAMC,MAAgC,CAAC;IACvC,KAAK,MAAM,CAACC,KAAKP,MAAM,IAAIQ,OAAOC,OAAO,CAACJ,MAAO;QAC7CC,GAAG,CAACC,IAAI,GAAG,OAAOP,UAAU,WAAWD,kBAAkBC,SAASA;IACtE;IACA,OAAOM;AACX;AAEA,SAASI,cAAcC,IAAsB;IACzC,+FAA+F,GAC/F,OAAO;QACHC,MAAMD,KAAKC,IAAI;QACfC,UAAUF,KAAKG,OAAO;QACtBC,aAAaJ,KAAKK,UAAU;QAC5BC,SAASN,KAAKM,OAAO;QACrB,GAAIN,KAAKO,MAAM,GAAG;YAAEA,QAAQP,KAAKO,MAAM;QAAC,IAAI,CAAC,CAAC;QAC9C,GAAIP,KAAKQ,UAAU,KAAKC,YAAY;YAAEC,aAAaV,KAAKQ,UAAU;QAAC,IAAI,CAAC,CAAC;QACzE,GAAIR,KAAKW,UAAU,GAAG;YAAEA,YAAYX,KAAKW,UAAU;QAAC,IAAI,CAAC,CAAC;IAC9D;AACA,sDAAsD,GAC1D;AAEA,uEAAuE,GACvE,OAAO,SAASC,cAAcC,OAAqB;IAC/C,MAAMC,IAAID,QAAQE,OAAO;IACzB,+FAA+F,GAC/F9B,aAAa+B,SAAS,CAACpC,gBAAgB;QACnCmC,SAAS;YACLd,MAAMb,kBAAkB0B,EAAEb,IAAI;YAC9BgB,MAAM7B,kBAAkB0B,EAAEG,IAAI;YAC9BC,OAAO9B,kBAAkB0B,EAAEI,KAAK;YAChCC,SAAS/B,kBAAkB0B,EAAEK,OAAO;YACpCb,SAASQ,EAAER,OAAO;YAClB,GAAIQ,EAAEP,MAAM,GAAG;gBAAEA,QAAQnB,kBAAkB0B,EAAEP,MAAM;YAAE,IAAI,CAAC,CAAC;YAC3DH,aAAaU,EAAET,UAAU;YACzBe,OAAON,EAAEM,KAAK,CAACC,GAAG,CAACtB;YACnB,GAAIe,EAAEQ,QAAQ,GAAG;gBAAEA,UAAUR,EAAEQ,QAAQ;YAAC,IAAI,CAAC,CAAC;YAC9C,GAAIR,EAAEpB,IAAI,GAAG;gBAAEA,MAAMD,cAAcqB,EAAEpB,IAAI;YAAE,IAAI,CAAC,CAAC;QACrD;IACJ;AACA,sDAAsD,GAC1D"}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@servicetitan/journey",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Critical user journey monitoring — reports good/bad/excluded verdicts to Datadog RUM as a custom action",
|
|
5
|
+
"homepage": "https://docs.st.dev/docs/frontend/journey",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/servicetitan/anvil-uikit-contrib.git",
|
|
9
|
+
"directory": "packages/journey"
|
|
10
|
+
},
|
|
11
|
+
"sideEffects": [
|
|
12
|
+
"./dist/config.js",
|
|
13
|
+
"./src/config.ts"
|
|
14
|
+
],
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"default": "./dist/index.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"main": "./dist/index.js",
|
|
22
|
+
"typings": "./dist/index.d.ts",
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"src"
|
|
26
|
+
],
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@datadog/browser-rum": "^7.5.0",
|
|
29
|
+
"@testing-library/jest-dom": "^6.4.2",
|
|
30
|
+
"@testing-library/react": "^14.2.1",
|
|
31
|
+
"@types/react": "~18.2.55",
|
|
32
|
+
"react": "^18.2.0"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@datadog/browser-rum": ">=6.19.0",
|
|
36
|
+
"react": ">=17.0.2"
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
41
|
+
"cli": {
|
|
42
|
+
"webpack": false
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { resetJourney } from '../core';
|
|
2
|
+
import { sendToDatadog } from '../sinks/datadog';
|
|
3
|
+
|
|
4
|
+
/** Standard journey def used across engine suites. */
|
|
5
|
+
export const baseJourneyDef = {
|
|
6
|
+
name: 'journey-under-test',
|
|
7
|
+
team: 'platform',
|
|
8
|
+
group: 'test',
|
|
9
|
+
service: 'journey',
|
|
10
|
+
timeoutMs: 0,
|
|
11
|
+
} as const;
|
|
12
|
+
|
|
13
|
+
/** Last emitted journey payload from a sink mock. */
|
|
14
|
+
export function lastJourney(emitMock: jest.Mock) {
|
|
15
|
+
return emitMock.mock.calls[emitMock.mock.calls.length - 1][0].journey;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Wire `emitMock` as the only sink and restore package defaults after each test.
|
|
20
|
+
* Call once at the top of a `describe` block.
|
|
21
|
+
*/
|
|
22
|
+
export function useJourneyTestRuntime(emitMock: jest.Mock) {
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
resetJourney({
|
|
25
|
+
sinks: [emitMock],
|
|
26
|
+
requestTimeouts: { defaultMs: 4_000, endpoints: [] },
|
|
27
|
+
});
|
|
28
|
+
emitMock.mockClear();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
afterEach(() => {
|
|
32
|
+
resetJourney({
|
|
33
|
+
sinks: [sendToDatadog],
|
|
34
|
+
requestTimeouts: { defaultMs: 4_000, endpoints: [] },
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/** @jest-environment jsdom */
|
|
2
|
+
import { baseJourneyDef, useJourneyTestRuntime } from '../__mocks__/test-runtime';
|
|
3
|
+
import { activeJourneyStep, completeJourney, journeyStep, startJourney } from '../core';
|
|
4
|
+
|
|
5
|
+
const emitMock = jest.fn();
|
|
6
|
+
const baseDef = { ...baseJourneyDef, name: 'ambient-a' };
|
|
7
|
+
const otherDef = { ...baseJourneyDef, name: 'ambient-b' };
|
|
8
|
+
|
|
9
|
+
describe('[journey] activeJourneyStep', () => {
|
|
10
|
+
useJourneyTestRuntime(emitMock);
|
|
11
|
+
|
|
12
|
+
describe('around a single step', () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
startJourney(baseDef);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('is set only while the step is running', async () => {
|
|
18
|
+
expect(activeJourneyStep()).toBeNull();
|
|
19
|
+
|
|
20
|
+
await journeyStep(baseDef.name, 'do-work', () => {
|
|
21
|
+
expect(activeJourneyStep()).not.toBeNull();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
expect(activeJourneyStep()).toBeNull();
|
|
25
|
+
completeJourney(baseDef.name);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('when two steps overlap', () => {
|
|
30
|
+
let releaseA!: () => void;
|
|
31
|
+
let stepA: Promise<unknown>;
|
|
32
|
+
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
startJourney(baseDef);
|
|
35
|
+
startJourney(otherDef);
|
|
36
|
+
|
|
37
|
+
const gateA = new Promise<void>(r => {
|
|
38
|
+
releaseA = r;
|
|
39
|
+
});
|
|
40
|
+
stepA = journeyStep(baseDef.name, 'load', async () => {
|
|
41
|
+
await gateA;
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
afterEach(async () => {
|
|
46
|
+
releaseA();
|
|
47
|
+
await stepA;
|
|
48
|
+
completeJourney(baseDef.name);
|
|
49
|
+
completeJourney(otherDef.name);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const subject = () => journeyStep(otherDef.name, 'book', () => activeJourneyStep());
|
|
53
|
+
|
|
54
|
+
test('returns null', async () => {
|
|
55
|
+
await expect(subject()).resolves.toBeNull();
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('when the first-finishing sibling ends first', () => {
|
|
60
|
+
let releaseB!: () => void;
|
|
61
|
+
let stepB: Promise<unknown>;
|
|
62
|
+
let ambientWhileBAlone: ReturnType<typeof activeJourneyStep> | 'unset';
|
|
63
|
+
|
|
64
|
+
beforeEach(() => {
|
|
65
|
+
startJourney(baseDef);
|
|
66
|
+
startJourney(otherDef);
|
|
67
|
+
ambientWhileBAlone = 'unset';
|
|
68
|
+
|
|
69
|
+
const gateB = new Promise<void>(r => {
|
|
70
|
+
releaseB = r;
|
|
71
|
+
});
|
|
72
|
+
stepB = journeyStep(otherDef.name, 'book', async () => {
|
|
73
|
+
await gateB;
|
|
74
|
+
ambientWhileBAlone = activeJourneyStep();
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
afterEach(async () => {
|
|
79
|
+
releaseB();
|
|
80
|
+
await stepB;
|
|
81
|
+
completeJourney(baseDef.name);
|
|
82
|
+
completeJourney(otherDef.name);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('keeps ambient on the remaining step', async () => {
|
|
86
|
+
await journeyStep(baseDef.name, 'load', () => {
|
|
87
|
+
expect(activeJourneyStep()).toBeNull();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
releaseB();
|
|
91
|
+
await stepB;
|
|
92
|
+
|
|
93
|
+
expect(ambientWhileBAlone).not.toBeNull();
|
|
94
|
+
expect(ambientWhileBAlone).not.toBe('unset');
|
|
95
|
+
if (ambientWhileBAlone === 'unset' || ambientWhileBAlone == null) {
|
|
96
|
+
throw new Error('expected ambient step');
|
|
97
|
+
}
|
|
98
|
+
expect(ambientWhileBAlone.name).toBe('book');
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|