nstdlib-nightly
Version:
Node.js standard library converted to runtime-agnostic ES modules.
255 lines (226 loc) • 6.58 kB
JavaScript
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/perf/usertiming.js
import {
PerformanceEntry,
kSkipThrow,
} from "nstdlib/lib/internal/perf/performance_entry";
import { now } from "nstdlib/lib/internal/perf/utils";
import { enqueue, bufferUserTiming } from "nstdlib/lib/internal/perf/observe";
import * as nodeTiming from "nstdlib/lib/internal/perf/nodetiming";
import {
validateNumber,
validateObject,
validateString,
validateInternalField,
} from "nstdlib/lib/internal/validators";
import { codes as __codes__ } from "nstdlib/lib/internal/errors";
import { structuredClone } from "nstdlib/stub/binding/messaging";
import {
lazyDOMException,
kEnumerableProperty,
} from "nstdlib/lib/internal/util";
const {
ERR_ILLEGAL_CONSTRUCTOR,
ERR_INVALID_ARG_VALUE,
ERR_MISSING_ARGS,
ERR_PERFORMANCE_INVALID_TIMESTAMP,
ERR_PERFORMANCE_MEASURE_INVALID_OPTIONS,
} = __codes__;
const kDetail = Symbol("kDetail");
const markTimings = new Map();
const nodeTimingReadOnlyAttributes = new Set(
new (Array.prototype[Symbol.iterator]())([
"nodeStart",
"v8Start",
"environment",
"loopStart",
"loopExit",
"bootstrapComplete",
]),
);
function getMark(name) {
if (name === undefined) return;
if (typeof name === "number") {
if (name < 0) throw new ERR_PERFORMANCE_INVALID_TIMESTAMP(name);
return name;
}
name = `${name}`;
if (nodeTimingReadOnlyAttributes.has(name)) return nodeTiming[name];
const ts = markTimings.get(name);
if (ts === undefined)
throw lazyDOMException(
`The "${name}" performance mark has not been set`,
"SyntaxError",
);
return ts;
}
class PerformanceMark extends PerformanceEntry {
constructor(name, options = undefined) {
if (arguments.length === 0) {
throw new ERR_MISSING_ARGS("name");
}
name = `${name}`;
if (nodeTimingReadOnlyAttributes.has(name))
throw new ERR_INVALID_ARG_VALUE("name", name);
if (options != null) {
validateObject(options, "options");
}
const startTime = options?.startTime ?? now();
validateNumber(startTime, "startTime");
if (startTime < 0) throw new ERR_PERFORMANCE_INVALID_TIMESTAMP(startTime);
markTimings.set(name, startTime);
let detail = options?.detail;
detail = detail != null ? structuredClone(detail) : null;
super(kSkipThrow, name, "mark", startTime, 0);
this[kDetail] = detail;
}
get detail() {
validateInternalField(this, kDetail, "PerformanceMark");
return this[kDetail];
}
toJSON() {
return {
name: this.name,
entryType: this.entryType,
startTime: this.startTime,
duration: this.duration,
detail: this[kDetail],
};
}
}
Object.defineProperties(PerformanceMark.prototype, {
detail: kEnumerableProperty,
[Symbol.toStringTag]: {
__proto__: null,
configurable: true,
value: "PerformanceMark",
},
});
class PerformanceMeasure extends PerformanceEntry {
constructor(
skipThrowSymbol = undefined,
name = undefined,
type = undefined,
start = undefined,
duration = undefined,
) {
if (skipThrowSymbol !== kSkipThrow) {
throw new ERR_ILLEGAL_CONSTRUCTOR();
}
super(skipThrowSymbol, name, type, start, duration);
}
get detail() {
validateInternalField(this, kDetail, "PerformanceMeasure");
return this[kDetail];
}
toJSON() {
return {
name: this.name,
entryType: this.entryType,
startTime: this.startTime,
duration: this.duration,
detail: this[kDetail],
};
}
}
Object.defineProperties(PerformanceMeasure.prototype, {
detail: kEnumerableProperty,
[Symbol.toStringTag]: {
__proto__: null,
configurable: true,
value: "PerformanceMeasure",
},
});
function createPerformanceMeasure(name, start, duration, detail) {
const measure = new PerformanceMeasure(
kSkipThrow,
name,
"measure",
start,
duration,
);
measure[kDetail] = detail;
return measure;
}
function mark(name, options) {
const mark = new PerformanceMark(name, options);
enqueue(mark);
bufferUserTiming(mark);
return mark;
}
function calculateStartDuration(startOrMeasureOptions, endMark) {
startOrMeasureOptions ??= 0;
let start;
let end;
let duration;
let optionsValid = false;
if (typeof startOrMeasureOptions === "object") {
({ start, end, duration } = startOrMeasureOptions);
optionsValid = start !== undefined || end !== undefined;
}
if (optionsValid) {
if (endMark !== undefined) {
throw new ERR_PERFORMANCE_MEASURE_INVALID_OPTIONS(
"endMark must not be specified",
);
}
if (start === undefined && end === undefined) {
throw new ERR_PERFORMANCE_MEASURE_INVALID_OPTIONS(
"One of options.start or options.end is required",
);
}
if (start !== undefined && end !== undefined && duration !== undefined) {
throw new ERR_PERFORMANCE_MEASURE_INVALID_OPTIONS(
"Must not have options.start, options.end, and " +
"options.duration specified",
);
}
}
if (endMark !== undefined) {
end = getMark(endMark);
} else if (optionsValid && end !== undefined) {
end = getMark(end);
} else if (optionsValid && start !== undefined && duration !== undefined) {
end = getMark(start) + getMark(duration);
} else {
end = now();
}
if (typeof startOrMeasureOptions === "string") {
start = getMark(startOrMeasureOptions);
} else if (optionsValid && start !== undefined) {
start = getMark(start);
} else if (optionsValid && duration !== undefined && end !== undefined) {
start = end - getMark(duration);
} else {
start = 0;
}
duration = end - start;
return { start, duration };
}
function measure(name, startOrMeasureOptions, endMark) {
validateString(name, "name");
const { start, duration } = calculateStartDuration(
startOrMeasureOptions,
endMark,
);
let detail = startOrMeasureOptions?.detail;
detail = detail != null ? structuredClone(detail) : null;
const measure = createPerformanceMeasure(name, start, duration, detail);
enqueue(measure);
bufferUserTiming(measure);
return measure;
}
function clearMarkTimings(name) {
if (name !== undefined) {
name = `${name}`;
if (nodeTimingReadOnlyAttributes.has(name))
throw new ERR_INVALID_ARG_VALUE("name", name);
markTimings.delete(name);
return;
}
markTimings.clear();
}
export { PerformanceMark };
export { PerformanceMeasure };
export { clearMarkTimings };
export { mark };
export { measure };