@contrast/core
Advanced tools
Comparing version 1.31.1 to 1.31.2
@@ -15,3 +15,3 @@ /* | ||
*/ | ||
// @ts-check | ||
'use strict'; | ||
@@ -22,2 +22,5 @@ | ||
/** @typedef {import('.').Frame} Frame */ | ||
/** @typedef {import('.').CreateSnapshotOpts} CreateSnapshotOpts */ | ||
const EVAL_ORIGIN_REGEX = /\((.*?):(\d+):\d+\)/; | ||
@@ -33,8 +36,10 @@ | ||
core.captureStacktrace = function(...args) { | ||
return stacktraceFactory.captureStacktrace(...args); | ||
/** @type {StacktraceFactory['captureStacktrace']} */ | ||
core.captureStacktrace = function (obj, opts, key) { | ||
return stacktraceFactory.captureStacktrace(obj, opts, key); | ||
}; | ||
core.createSnapshot = function(...args) { | ||
return stacktraceFactory.createSnapshot(...args); | ||
/** @type {StacktraceFactory['createSnapshot']} */ | ||
core.createSnapshot = function (opts) { | ||
return stacktraceFactory.createSnapshot(opts); | ||
}; | ||
@@ -45,8 +50,9 @@ | ||
/** | ||
* The factory will set stacktrace limit for stackframe lists created by it. | ||
* @param {number} stackTraceLimit set the stack trace limit | ||
* @param {function} isAgentPath function indicating if path is agent code | ||
*/ | ||
class StacktraceFactory { | ||
/** | ||
* The factory will set stacktrace limit for stackframe lists created by it. | ||
* @param {Object} opts | ||
* @param {number} opts.stackTraceLimit set the stack trace limit | ||
* @param {(path: string) => boolean} opts.isAgentPath function indicating if path is agent code | ||
*/ | ||
constructor({ stackTraceLimit, isAgentPath }) { | ||
@@ -57,2 +63,9 @@ this.stackTraceLimit = stackTraceLimit; | ||
/** | ||
* @template T | ||
* @param {T} obj | ||
* @param {CreateSnapshotOpts} opts | ||
* @param {string} key | ||
* @returns {T} | ||
*/ | ||
captureStacktrace(obj, opts, key = 'stack') { | ||
@@ -80,50 +93,60 @@ let stack; | ||
/** | ||
* @param {Frame} frame | ||
* @returns {boolean} | ||
*/ | ||
shouldAppendFrame(frame) { | ||
return ( | ||
!!frame.file && | ||
!this.isAgentPath(frame.file) && | ||
!`${frame.type}${frame.method}`.includes('ContrastMethods') | ||
); | ||
} | ||
/** | ||
* Creates a function that will build a stacktrace when invoked. It will keep | ||
* an error in a closure whose stack will be generated and processed when the | ||
* returned function executes. The result will be cached. | ||
* @param {object} params | ||
* @param {function} params.limitFn The constructorOpt param used when creating stack | ||
* @returns {function} | ||
* @param {CreateSnapshotOpts} params | ||
* @returns {() => Frame[]} | ||
*/ | ||
createSnapshot({ constructorOpt, prependFrames } = {}) { | ||
const { isAgentPath } = this; | ||
const target = {}; | ||
this.appendStackGetter(target, constructorOpt); | ||
let ret; | ||
let frames; | ||
/** | ||
* Generates array of frames from `target`'s `stack` getter | ||
* @returns {array} | ||
* @returns {Frame[]} | ||
*/ | ||
function snapshot() { | ||
if (!ret) { | ||
const callsites = StacktraceFactory.generateCallsites(target); | ||
/* eslint-disable complexity */ | ||
ret = (callsites || []).reduce((acc, callsite) => { | ||
if (StacktraceFactory.isCallsiteValid(callsite)) { | ||
const frame = StacktraceFactory.makeFrame(callsite); | ||
if ( | ||
frame.file && | ||
!`${frame.type}${frame.method}`.includes('ContrastMethods') | ||
) { | ||
if (!isAgentPath(frame.file)) { | ||
const snapshot = () => { | ||
if (!frames) { | ||
// @ts-expect-error target has had a stack getter appended above. | ||
const callsites = StacktraceFactory.generateCallsites(target) ?? []; | ||
frames = callsites.reduce( | ||
/** | ||
* @param {Frame[]} acc | ||
* @param {NodeJS.CallSite} callsite | ||
*/ | ||
(acc, callsite) => { | ||
if (StacktraceFactory.isCallsiteValid(callsite)) { | ||
const frame = StacktraceFactory.makeFrame(callsite); | ||
if (this.shouldAppendFrame(frame)) { | ||
acc.push(frame); | ||
} | ||
} | ||
} | ||
return acc; | ||
}, []); | ||
return acc; | ||
}, | ||
[], | ||
); | ||
} | ||
if (!prependFrames) return ret; | ||
if (!prependFrames) return frames; | ||
return [ | ||
...prependFrames.map((f) => (typeof f == 'function' ? fnInspect.funcInfo(f) : f)), | ||
...ret | ||
...frames, | ||
]; | ||
} | ||
}; | ||
@@ -135,12 +158,16 @@ return snapshot; | ||
* Based on stacktrace limit and constructor opt, will append a stack getter | ||
* @param {} error | ||
* @param {} limitFn | ||
* @param {any} obj | ||
* @param {Function=} constructorOpt | ||
*/ | ||
appendStackGetter(error, constructorOpt) { | ||
appendStackGetter(obj, constructorOpt) { | ||
const { stackTraceLimit } = Error; | ||
Error.stackTraceLimit = this.stackTraceLimit; | ||
Error.captureStackTrace(error, constructorOpt); | ||
Error.captureStackTrace(obj, constructorOpt); | ||
Error.stackTraceLimit = stackTraceLimit; | ||
} | ||
/** | ||
* @param {any} callsite | ||
* @returns {boolean} | ||
*/ | ||
static isCallsiteValid(callsite) { | ||
@@ -152,16 +179,28 @@ return callsite instanceof Callsite; | ||
* Creates an array frame objects from an array of Callsite instances | ||
* @param {} callsite | ||
* @returns {} | ||
* @param {NodeJS.CallSite} callsite | ||
* @returns {Frame} | ||
*/ | ||
static makeFrame(callsite) { | ||
let evalOrigin, file, lineNumber, method, type; | ||
/** @type {string | undefined} */ | ||
let evalOrigin; | ||
/** @type {string | undefined} */ | ||
let file; | ||
/** @type {number | null} */ | ||
let lineNumber = null; | ||
/** @type {string | null} */ | ||
let method = null; | ||
/** @type {string | null} */ | ||
let type; | ||
if (callsite.isEval()) { | ||
evalOrigin = StacktraceFactory.formatFileName(callsite.getEvalOrigin()); | ||
[, file, lineNumber] = evalOrigin.match(EVAL_ORIGIN_REGEX) || evalOrigin.endsWith('.ejs'); | ||
const matches = EVAL_ORIGIN_REGEX.exec(evalOrigin); | ||
if (matches) { | ||
file = matches[1]; | ||
lineNumber = parseInt(matches[2]); | ||
} | ||
} | ||
file = file || callsite.getFileName(); | ||
lineNumber = lineNumber || callsite.getLineNumber(); | ||
file = file ?? callsite.getFileName(); | ||
lineNumber = lineNumber ?? callsite.getLineNumber(); | ||
method = callsite.getFunctionName(); | ||
@@ -178,2 +217,6 @@ type = callsite.getTypeName(); | ||
/** | ||
* @param {string} fileName | ||
* @returns {string} | ||
*/ | ||
static formatFileName(fileName = '') { | ||
@@ -193,11 +236,11 @@ const cwd = `${process.cwd()}/`; | ||
* using an ephemeral monkey patch. | ||
* @param {object} error object with a `stack` getter property | ||
* @returns {} | ||
* @param {Error} error object with a `stack` getter property | ||
* @returns {NodeJS.CallSite[]} | ||
*/ | ||
static generateCallsites(error) { | ||
let callsites; | ||
let callsites = []; | ||
const { prepareStackTrace } = Error; | ||
Error.prepareStackTrace = function(_, _callsites) { | ||
Error.prepareStackTrace = function contrastPrepareStackTrace(_, _callsites) { | ||
callsites = _callsites; | ||
@@ -204,0 +247,0 @@ return _callsites; |
@@ -18,2 +18,16 @@ /* | ||
interface Frame { | ||
eval: string | undefined; | ||
file: string | undefined; | ||
lineNumber: number | null; | ||
method: string | null; | ||
type: string | null; | ||
} | ||
/* eslint-disable @typescript-eslint/ban-types */ | ||
interface CreateSnapshotOpts { | ||
constructorOpt?: Function; | ||
prependFrames?: (Function | Frame)[]; | ||
} | ||
export interface Core { | ||
@@ -25,4 +39,4 @@ agentName: string; | ||
captureStackTrace(...args: any[]): any; | ||
captureSnapshot(...args: any[]): any; | ||
captureStackTrace<T>(obj: T, opts: CreateSnapshotOpts, key: string): T; | ||
createSnapshot(opts?: CreateSnapshotOpts): () => Frame[]; | ||
@@ -29,0 +43,0 @@ isAgentPath(path: string): boolean; |
@@ -15,7 +15,4 @@ /* | ||
*/ | ||
'use strict'; | ||
/** | ||
*/ | ||
module.exports = function(core) { | ||
@@ -25,2 +22,5 @@ const { config } = core; | ||
/** | ||
* Returns true if the provided path matches any of the configured filters. | ||
* @param {string} path | ||
* @returns {boolean} | ||
*/ | ||
@@ -27,0 +27,0 @@ function isAgentPath(path) { |
{ | ||
"name": "@contrast/core", | ||
"version": "1.31.1", | ||
"version": "1.31.2", | ||
"description": "Preconfigured Contrast agent core services and models", | ||
@@ -5,0 +5,0 @@ "license": "SEE LICENSE IN LICENSE", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
37048
953