@zendesk/retrace
Version:
define and capture Product Operation Traces along with computed metrics with an optional friendly React beacon API
139 lines • 4.82 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.FMP = exports.Check = exports.Idle = exports.LongTask = exports.Render = void 0;
exports.makeEntries = makeEntries;
exports.getSpansFromTimeline = getSpansFromTimeline;
const Render = (name, duration, options = {}) => ({
entryType: 'component-render',
name,
duration,
startTime: options.startTime,
isIdle: (options.renderedOutput === 'content' ||
options.renderedOutput === 'error') ??
options.isIdle,
...options,
});
exports.Render = Render;
const LongTask = (duration, options = {}) => ({
entryType: 'longtask',
duration,
startTime: options.start,
name: 'task',
});
exports.LongTask = LongTask;
const Idle = (duration) => ({
entryType: 'idle',
duration,
});
exports.Idle = Idle;
exports.Check = {
entryType: 'mark',
name: 'check',
};
exports.FMP = {
entryType: 'fmp',
};
function makeEntries(events) {
const entries = [];
let currentTime = 0;
let fmpTime = null;
for (const event of events) {
const thisEventStartTime = 'startTime' in event && event.startTime !== undefined && event.startTime;
const eventStartTime = thisEventStartTime !== false ? thisEventStartTime : currentTime;
const eventDuration = 'duration' in event ? event.duration : 0;
switch (event.entryType) {
case 'idle':
break;
case 'fmp':
fmpTime = eventStartTime;
if (event.startTime === undefined)
fmpTime = currentTime;
// fallthrough on purpose
// eslint-disable-next-line no-fallthrough
default:
entries.push({
entryType: event.entryType,
name: 'name' in event ? event.name : event.entryType,
startTime: eventStartTime,
duration: eventDuration,
});
break;
}
// Update `currentTime` only if `startTime` is not predefined
if (thisEventStartTime === false) {
currentTime = eventStartTime + eventDuration;
}
}
return { entries, fmpTime };
}
function getSpansFromTimeline(_, ...exprs) {
const spans = [];
let fmpTime = null;
const stubs = exprs.filter((expr) => typeof expr !== 'number');
const allNumbers = exprs.filter((expr) => typeof expr === 'number');
let startTime;
let time;
if (allNumbers.length === stubs.length + 1) {
startTime = allNumbers[0];
time = allNumbers.slice(1);
}
else if (allNumbers.length === stubs.length) {
startTime = allNumbers[0];
time = allNumbers;
}
else {
throw new Error('Invalid timeline, mismatch of events and timestamps');
}
if (startTime === undefined) {
throw new Error('No time provided for the beginning of the timeline');
}
for (let i = 0; i < time.length; i++) {
const currentTime = time[i];
const stub = stubs[i];
if (!stub || typeof currentTime !== 'number') {
throw new Error('Invalid timeline, mismatch of events and timestamps');
}
if (stub.entryType === 'fmp') {
fmpTime = currentTime;
}
const now = 'startTime' in stub ? stub.startTime ?? currentTime : currentTime;
spans.push({
type: stub.entryType,
duration: 0,
name: `${stub.entryType}`,
...stub,
startTime: {
now,
epoch: now,
},
isIdle: 'isIdle' in stub
? stub.isIdle
: 'name' in stub
? stub.name?.includes('idle')
: undefined,
renderedOutput: 'renderedOutput' in stub
? stub.renderedOutput
: 'name' in stub
? stub.name?.includes('idle')
? 'content'
: 'loading'
: undefined,
performanceEntry: {
duration: 0,
name: `${stub.entryType}`,
...stub,
startTime: 'startTime' in stub ? stub.startTime ?? currentTime : currentTime,
toJSON: () => { },
},
id: `span-${i}-${now}-${stub.entryType}-${'duration' in stub ? stub.duration : 0}`,
});
}
return { spans, fmpTime };
}
// example usage
// const timeline = getEventsFromTimeline`
// Events: ----------${FMP}-----${Task(50)}-------${Task(100)}-------${Task(200)}-------${Check}
// Time: ${0} ${200} ${300} ${350} ${550} ${700}
// `
// console.log(timeline)
//# sourceMappingURL=makeTimeline.js.map