Sign In

posthog-node

Package Overview
Dependencies
Maintainers
22
Versions
311
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

posthog-node - npm Package Compare versions

Comparing version
5.47.6
to
5.47.7
+1
-1
dist/entrypoints/index.node.d.ts.map

@@ -1,1 +0,1 @@

{"version":3,"file":"index.node.d.ts","sourceRoot":"","sources":["../../src/entrypoints/index.node.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAM1B,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAChD,OAAO,EAAE,aAAa,IAAI,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAA;AAE9D,qBAAa,OAAQ,SAAQ,oBAAoB;IAC/C,YAAY,IAAI,MAAM;IAItB,SAAS,CAAC,iBAAiB,IAAI,cAAc;cAI1B,4BAA4B,IAAI,iBAAiB,CAAC,sBAAsB;CAa5F"}
{"version":3,"file":"index.node.d.ts","sourceRoot":"","sources":["../../src/entrypoints/index.node.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAM1B,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAChD,OAAO,EAAE,aAAa,IAAI,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAA;AAE9D,qBAAa,OAAQ,SAAQ,oBAAoB;IAC/C,YAAY,IAAI,MAAM;IAItB,SAAS,CAAC,iBAAiB,IAAI,cAAc;cAI1B,4BAA4B,IAAI,iBAAiB,CAAC,sBAAsB;CAiB5F"}

@@ -102,3 +102,3 @@ "use strict";

(0, _extensions_error_tracking_modifiers_module_node__WEBPACK_IMPORTED_MODULE_1__.createModulerModifier)(),
_extensions_error_tracking_modifiers_context_lines_node__WEBPACK_IMPORTED_MODULE_2__.addSourceContext,
(frames)=>(0, _extensions_error_tracking_modifiers_context_lines_node__WEBPACK_IMPORTED_MODULE_2__.addSourceContext)(frames, void 0, this._logger),
(0, _extensions_error_tracking_modifiers_relative_path_node__WEBPACK_IMPORTED_MODULE_3__.createRelativePathModifier)()

@@ -105,0 +105,0 @@ ]);

@@ -24,3 +24,3 @@ import { createModulerModifier } from "../extensions/error-tracking/modifiers/module.node.mjs";

createModulerModifier(),
addSourceContext,
(frames)=>addSourceContext(frames, void 0, this._logger),
createRelativePathModifier()

@@ -27,0 +27,0 @@ ]);

@@ -1,5 +0,9 @@

import { ErrorTracking as CoreErrorTracking } from '@posthog/core';
import { ErrorTracking as CoreErrorTracking, type Logger } from '@posthog/core';
import { type FileHandle } from 'node:fs/promises';
export declare const MAX_CONTEXTLINES_COLNO: number;
export declare const MAX_CONTEXTLINES_LINENO: number;
export declare function addSourceContext(frames: CoreErrorTracking.StackFrame[]): Promise<CoreErrorTracking.StackFrame[]>;
export declare const MAX_CONTEXTLINES_FILE_SIZE: number;
type OpenSourceFile = (path: string, flags: number) => Promise<FileHandle>;
export declare function addSourceContext(frames: CoreErrorTracking.StackFrame[], openSourceFile?: OpenSourceFile, logger?: Pick<Logger, 'debug'>): Promise<CoreErrorTracking.StackFrame[]>;
export {};
//# sourceMappingURL=context-lines.node.d.ts.map

@@ -1,1 +0,1 @@

{"version":3,"file":"context-lines.node.d.ts","sourceRoot":"","sources":["../../../../src/extensions/error-tracking/modifiers/context-lines.node.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,IAAI,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAUlE,eAAO,MAAM,sBAAsB,EAAE,MAAa,CAAA;AAClD,eAAO,MAAM,uBAAuB,EAAE,MAAc,CAAA;AAIpD,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,iBAAiB,CAAC,UAAU,EAAE,GACrC,OAAO,CAAC,iBAAiB,CAAC,UAAU,EAAE,CAAC,CAuEzC"}
{"version":3,"file":"context-lines.node.d.ts","sourceRoot":"","sources":["../../../../src/extensions/error-tracking/modifiers/context-lines.node.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,IAAI,iBAAiB,EAAE,KAAK,MAAM,EAAE,MAAM,eAAe,CAAA;AAE/E,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAUxD,eAAO,MAAM,sBAAsB,EAAE,MAAa,CAAA;AAClD,eAAO,MAAM,uBAAuB,EAAE,MAAc,CAAA;AACpD,eAAO,MAAM,0BAA0B,EAAE,MAAyB,CAAA;AAGlE,KAAK,cAAc,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAA;AAE1E,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,iBAAiB,CAAC,UAAU,EAAE,EACtC,cAAc,GAAE,cAAqB,EACrC,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC,iBAAiB,CAAC,UAAU,EAAE,CAAC,CAqFzC"}

@@ -27,8 +27,11 @@ "use strict";

__webpack_require__.d(__webpack_exports__, {
MAX_CONTEXTLINES_LINENO: ()=>MAX_CONTEXTLINES_LINENO,
MAX_CONTEXTLINES_FILE_SIZE: ()=>MAX_CONTEXTLINES_FILE_SIZE,
MAX_CONTEXTLINES_COLNO: ()=>MAX_CONTEXTLINES_COLNO,
addSourceContext: ()=>addSourceContext
addSourceContext: ()=>addSourceContext,
MAX_CONTEXTLINES_LINENO: ()=>MAX_CONTEXTLINES_LINENO
});
const core_namespaceObject = require("@posthog/core");
const external_node_fs_namespaceObject = require("node:fs");
const promises_namespaceObject = require("node:fs/promises");
const external_node_path_namespaceObject = require("node:path");
const external_node_readline_namespaceObject = require("node:readline");

@@ -40,4 +43,9 @@ const LRU_FILE_CONTENTS_CACHE = new core_namespaceObject.ErrorTracking.ReduceableCache(25);

const MAX_CONTEXTLINES_LINENO = 10000;
async function addSourceContext(frames) {
const MAX_CONTEXTLINES_FILE_SIZE = 10485760;
async function addSourceContext(frames, openSourceFile = promises_namespaceObject.open, logger) {
const filesToLines = {};
let basePath;
try {
basePath = process.cwd();
} catch {}
for(let i = frames.length - 1; i >= 0; i--){

@@ -47,2 +55,3 @@ const frame = frames[i];

if (!frame || 'string' != typeof filename || 'number' != typeof frame.lineno || shouldSkipContextLinesForFile(filename) || shouldSkipContextLinesForFrame(frame)) continue;
if (!(0, external_node_path_namespaceObject.isAbsolute)(filename) && void 0 === basePath) continue;
const filesToLinesOutput = filesToLines[filename];

@@ -56,3 +65,5 @@ if (!filesToLinesOutput) filesToLines[filename] = [];

for (const file of files){
if (LRU_FILE_CONTENTS_FS_READ_FAILED.get(file)) continue;
const cacheKey = makeSourceCacheKey(file, basePath);
if (void 0 === cacheKey) continue;
if (LRU_FILE_CONTENTS_FS_READ_FAILED.get(cacheKey)) continue;
const filesToLineRanges = filesToLines[file];

@@ -62,36 +73,76 @@ if (!filesToLineRanges) continue;

const ranges = makeLineReaderRanges(filesToLineRanges);
if (ranges.every((r)=>rangeExistsInContentCache(file, r))) continue;
const cache = emplace(LRU_FILE_CONTENTS_CACHE, file, {});
readlinePromises.push(getContextLinesFromFile(file, ranges, cache));
if (ranges.every((r)=>rangeExistsInContentCache(cacheKey, r))) continue;
const cache = emplace(LRU_FILE_CONTENTS_CACHE, cacheKey, {});
readlinePromises.push(getContextLinesFromFile(file, ranges, cache, cacheKey, openSourceFile, logger));
}
await Promise.all(readlinePromises).catch(()=>{});
if (frames && frames.length > 0) addSourceContextToFrames(frames, LRU_FILE_CONTENTS_CACHE);
if (frames && frames.length > 0) addSourceContextToFrames(frames, LRU_FILE_CONTENTS_CACHE, basePath);
LRU_FILE_CONTENTS_CACHE.reduce();
return frames;
}
function getContextLinesFromFile(path, ranges, output) {
async function openRegularSourceFile(path, openSourceFile, logger) {
let fileHandle;
let isValid = false;
try {
fileHandle = await openSourceFile(path, external_node_fs_namespaceObject.constants.O_RDONLY | external_node_fs_namespaceObject.constants.O_NONBLOCK);
const fileStat = await fileHandle.stat();
if (!fileStat.isFile()) return;
if (fileStat.size > MAX_CONTEXTLINES_FILE_SIZE) return void logger?.debug(`Skipping source context for oversized file ${path}: ${fileStat.size} bytes exceeds ${MAX_CONTEXTLINES_FILE_SIZE}`);
isValid = true;
return fileHandle;
} catch {
return;
} finally{
if (fileHandle && !isValid) await fileHandle.close().catch(()=>{});
}
}
async function getContextLinesFromFile(path, ranges, output, cacheKey, openSourceFile, logger) {
const fileHandle = await openRegularSourceFile(path, openSourceFile, logger);
if (void 0 === fileHandle) return void LRU_FILE_CONTENTS_FS_READ_FAILED.set(cacheKey, 1);
const openedFileHandle = fileHandle;
return new Promise((resolve)=>{
const stream = (0, external_node_fs_namespaceObject.createReadStream)(path);
const lineReaded = (0, external_node_readline_namespaceObject.createInterface)({
input: stream
});
function destroyStreamAndResolve() {
stream.destroy();
resolve();
let finished = false;
function destroyStreamAndResolve(stream) {
if (finished) return;
finished = true;
stream?.destroy();
openedFileHandle.close().then(resolve, resolve);
}
let stream;
try {
stream = openedFileHandle.createReadStream({
autoClose: false,
start: 0,
end: MAX_CONTEXTLINES_FILE_SIZE - 1
});
} catch {
LRU_FILE_CONTENTS_FS_READ_FAILED.set(cacheKey, 1);
destroyStreamAndResolve();
return;
}
let lineReaded;
try {
lineReaded = (0, external_node_readline_namespaceObject.createInterface)({
input: stream
});
} catch {
LRU_FILE_CONTENTS_FS_READ_FAILED.set(cacheKey, 1);
destroyStreamAndResolve(stream);
return;
}
let lineNumber = 0;
let currentRangeIndex = 0;
const range = ranges[currentRangeIndex];
if (void 0 === range) return void destroyStreamAndResolve();
if (void 0 === range) return void destroyStreamAndResolve(stream);
let rangeStart = range[0];
let rangeEnd = range[1];
function onStreamError() {
LRU_FILE_CONTENTS_FS_READ_FAILED.set(path, 1);
LRU_FILE_CONTENTS_FS_READ_FAILED.set(cacheKey, 1);
lineReaded.close();
lineReaded.removeAllListeners();
destroyStreamAndResolve();
destroyStreamAndResolve(stream);
}
stream.on('error', onStreamError);
lineReaded.on('error', onStreamError);
lineReaded.on('close', destroyStreamAndResolve);
lineReaded.on('close', ()=>destroyStreamAndResolve(stream));
lineReaded.on('line', (line)=>{

@@ -120,5 +171,6 @@ lineNumber++;

}
function addSourceContextToFrames(frames, cache) {
function addSourceContextToFrames(frames, cache, basePath) {
for (const frame of frames)if (frame.filename && void 0 === frame.context_line && 'number' == typeof frame.lineno) {
const contents = cache.get(frame.filename);
const cacheKey = makeSourceCacheKey(frame.filename, basePath);
const contents = void 0 === cacheKey ? void 0 : cache.get(cacheKey);
if (void 0 === contents) continue;

@@ -159,4 +211,14 @@ addContextToFrame(frame.lineno, frame, contents);

}
function rangeExistsInContentCache(file, range) {
const contents = LRU_FILE_CONTENTS_CACHE.get(file);
function makeSourceCacheKey(path, basePath) {
if ((0, external_node_path_namespaceObject.isAbsolute)(path)) return JSON.stringify([
null,
path
]);
return void 0 === basePath ? void 0 : JSON.stringify([
basePath,
path
]);
}
function rangeExistsInContentCache(cacheKey, range) {
const contents = LRU_FILE_CONTENTS_CACHE.get(cacheKey);
if (void 0 === contents) return false;

@@ -225,2 +287,3 @@ for(let i = range[0]; i <= range[1]; i++)if (void 0 === contents[i]) return false;

exports.MAX_CONTEXTLINES_COLNO = __webpack_exports__.MAX_CONTEXTLINES_COLNO;
exports.MAX_CONTEXTLINES_FILE_SIZE = __webpack_exports__.MAX_CONTEXTLINES_FILE_SIZE;
exports.MAX_CONTEXTLINES_LINENO = __webpack_exports__.MAX_CONTEXTLINES_LINENO;

@@ -230,2 +293,3 @@ exports.addSourceContext = __webpack_exports__.addSourceContext;

"MAX_CONTEXTLINES_COLNO",
"MAX_CONTEXTLINES_FILE_SIZE",
"MAX_CONTEXTLINES_LINENO",

@@ -232,0 +296,0 @@ "addSourceContext"

import { ErrorTracking } from "@posthog/core";
import { createReadStream } from "node:fs";
import { constants } from "node:fs";
import { open as promises_open } from "node:fs/promises";
import { isAbsolute } from "node:path";
import { createInterface } from "node:readline";

@@ -9,4 +11,9 @@ const LRU_FILE_CONTENTS_CACHE = new ErrorTracking.ReduceableCache(25);

const MAX_CONTEXTLINES_LINENO = 10000;
async function addSourceContext(frames) {
const MAX_CONTEXTLINES_FILE_SIZE = 10485760;
async function addSourceContext(frames, openSourceFile = promises_open, logger) {
const filesToLines = {};
let basePath;
try {
basePath = process.cwd();
} catch {}
for(let i = frames.length - 1; i >= 0; i--){

@@ -16,2 +23,3 @@ const frame = frames[i];

if (!frame || 'string' != typeof filename || 'number' != typeof frame.lineno || shouldSkipContextLinesForFile(filename) || shouldSkipContextLinesForFrame(frame)) continue;
if (!isAbsolute(filename) && void 0 === basePath) continue;
const filesToLinesOutput = filesToLines[filename];

@@ -25,3 +33,5 @@ if (!filesToLinesOutput) filesToLines[filename] = [];

for (const file of files){
if (LRU_FILE_CONTENTS_FS_READ_FAILED.get(file)) continue;
const cacheKey = makeSourceCacheKey(file, basePath);
if (void 0 === cacheKey) continue;
if (LRU_FILE_CONTENTS_FS_READ_FAILED.get(cacheKey)) continue;
const filesToLineRanges = filesToLines[file];

@@ -31,36 +41,76 @@ if (!filesToLineRanges) continue;

const ranges = makeLineReaderRanges(filesToLineRanges);
if (ranges.every((r)=>rangeExistsInContentCache(file, r))) continue;
const cache = emplace(LRU_FILE_CONTENTS_CACHE, file, {});
readlinePromises.push(getContextLinesFromFile(file, ranges, cache));
if (ranges.every((r)=>rangeExistsInContentCache(cacheKey, r))) continue;
const cache = emplace(LRU_FILE_CONTENTS_CACHE, cacheKey, {});
readlinePromises.push(getContextLinesFromFile(file, ranges, cache, cacheKey, openSourceFile, logger));
}
await Promise.all(readlinePromises).catch(()=>{});
if (frames && frames.length > 0) addSourceContextToFrames(frames, LRU_FILE_CONTENTS_CACHE);
if (frames && frames.length > 0) addSourceContextToFrames(frames, LRU_FILE_CONTENTS_CACHE, basePath);
LRU_FILE_CONTENTS_CACHE.reduce();
return frames;
}
function getContextLinesFromFile(path, ranges, output) {
async function openRegularSourceFile(path, openSourceFile, logger) {
let fileHandle;
let isValid = false;
try {
fileHandle = await openSourceFile(path, constants.O_RDONLY | constants.O_NONBLOCK);
const fileStat = await fileHandle.stat();
if (!fileStat.isFile()) return;
if (fileStat.size > MAX_CONTEXTLINES_FILE_SIZE) return void logger?.debug(`Skipping source context for oversized file ${path}: ${fileStat.size} bytes exceeds ${MAX_CONTEXTLINES_FILE_SIZE}`);
isValid = true;
return fileHandle;
} catch {
return;
} finally{
if (fileHandle && !isValid) await fileHandle.close().catch(()=>{});
}
}
async function getContextLinesFromFile(path, ranges, output, cacheKey, openSourceFile, logger) {
const fileHandle = await openRegularSourceFile(path, openSourceFile, logger);
if (void 0 === fileHandle) return void LRU_FILE_CONTENTS_FS_READ_FAILED.set(cacheKey, 1);
const openedFileHandle = fileHandle;
return new Promise((resolve)=>{
const stream = createReadStream(path);
const lineReaded = createInterface({
input: stream
});
function destroyStreamAndResolve() {
stream.destroy();
resolve();
let finished = false;
function destroyStreamAndResolve(stream) {
if (finished) return;
finished = true;
stream?.destroy();
openedFileHandle.close().then(resolve, resolve);
}
let stream;
try {
stream = openedFileHandle.createReadStream({
autoClose: false,
start: 0,
end: MAX_CONTEXTLINES_FILE_SIZE - 1
});
} catch {
LRU_FILE_CONTENTS_FS_READ_FAILED.set(cacheKey, 1);
destroyStreamAndResolve();
return;
}
let lineReaded;
try {
lineReaded = createInterface({
input: stream
});
} catch {
LRU_FILE_CONTENTS_FS_READ_FAILED.set(cacheKey, 1);
destroyStreamAndResolve(stream);
return;
}
let lineNumber = 0;
let currentRangeIndex = 0;
const range = ranges[currentRangeIndex];
if (void 0 === range) return void destroyStreamAndResolve();
if (void 0 === range) return void destroyStreamAndResolve(stream);
let rangeStart = range[0];
let rangeEnd = range[1];
function onStreamError() {
LRU_FILE_CONTENTS_FS_READ_FAILED.set(path, 1);
LRU_FILE_CONTENTS_FS_READ_FAILED.set(cacheKey, 1);
lineReaded.close();
lineReaded.removeAllListeners();
destroyStreamAndResolve();
destroyStreamAndResolve(stream);
}
stream.on('error', onStreamError);
lineReaded.on('error', onStreamError);
lineReaded.on('close', destroyStreamAndResolve);
lineReaded.on('close', ()=>destroyStreamAndResolve(stream));
lineReaded.on('line', (line)=>{

@@ -89,5 +139,6 @@ lineNumber++;

}
function addSourceContextToFrames(frames, cache) {
function addSourceContextToFrames(frames, cache, basePath) {
for (const frame of frames)if (frame.filename && void 0 === frame.context_line && 'number' == typeof frame.lineno) {
const contents = cache.get(frame.filename);
const cacheKey = makeSourceCacheKey(frame.filename, basePath);
const contents = void 0 === cacheKey ? void 0 : cache.get(cacheKey);
if (void 0 === contents) continue;

@@ -128,4 +179,14 @@ addContextToFrame(frame.lineno, frame, contents);

}
function rangeExistsInContentCache(file, range) {
const contents = LRU_FILE_CONTENTS_CACHE.get(file);
function makeSourceCacheKey(path, basePath) {
if (isAbsolute(path)) return JSON.stringify([
null,
path
]);
return void 0 === basePath ? void 0 : JSON.stringify([
basePath,
path
]);
}
function rangeExistsInContentCache(cacheKey, range) {
const contents = LRU_FILE_CONTENTS_CACHE.get(cacheKey);
if (void 0 === contents) return false;

@@ -193,2 +254,2 @@ for(let i = range[0]; i <= range[1]; i++)if (void 0 === contents[i]) return false;

}
export { MAX_CONTEXTLINES_COLNO, MAX_CONTEXTLINES_LINENO, addSourceContext };
export { MAX_CONTEXTLINES_COLNO, MAX_CONTEXTLINES_FILE_SIZE, MAX_CONTEXTLINES_LINENO, addSourceContext };

@@ -1,2 +0,2 @@

export declare const version = "5.47.6";
export declare const version = "5.47.7";
//# sourceMappingURL=version.d.ts.map

@@ -29,3 +29,3 @@ "use strict";

});
const version = '5.47.6';
const version = '5.47.7';
exports.version = __webpack_exports__.version;

@@ -32,0 +32,0 @@ for(var __webpack_i__ in __webpack_exports__)if (-1 === [

@@ -1,2 +0,2 @@

const version = '5.47.6';
const version = '5.47.7';
export { version };
{
"name": "posthog-node",
"version": "5.47.6",
"version": "5.47.7",
"bugs": {

@@ -5,0 +5,0 @@ "url": "https://github.com/PostHog/posthog-js/issues"

@@ -30,5 +30,9 @@ export * from '../exports'

CoreErrorTracking.createStackParser('node:javascript', CoreErrorTracking.nodeStackLineParser),
[createModulerModifier(), addSourceContext, createRelativePathModifier()]
[
createModulerModifier(),
(frames) => addSourceContext(frames, undefined, this._logger),
createRelativePathModifier(),
]
)
}
}

@@ -5,4 +5,6 @@ // Portions of this file are derived from getsentry/sentry-javascript

import { ErrorTracking as CoreErrorTracking } from '@posthog/core'
import { createReadStream } from 'node:fs'
import { ErrorTracking as CoreErrorTracking, type Logger } from '@posthog/core'
import { constants, type ReadStream } from 'node:fs'
import { open, type FileHandle } from 'node:fs/promises'
import { isAbsolute } from 'node:path'
import { createInterface } from 'node:readline'

@@ -18,7 +20,11 @@

export const MAX_CONTEXTLINES_LINENO: number = 10000
export const MAX_CONTEXTLINES_FILE_SIZE: number = 10 * 1024 * 1024
type ReadlineRange = [start: number, end: number]
type OpenSourceFile = (path: string, flags: number) => Promise<FileHandle>
export async function addSourceContext(
frames: CoreErrorTracking.StackFrame[]
frames: CoreErrorTracking.StackFrame[],
openSourceFile: OpenSourceFile = open,
logger?: Pick<Logger, 'debug'>
): Promise<CoreErrorTracking.StackFrame[]> {

@@ -28,2 +34,8 @@ // keep a lookup map of which files we've already enqueued to read,

const filesToLines: Record<string, number[]> = {}
let basePath: string | undefined
try {
basePath = process.cwd()
} catch {
// Absolute source paths can still be processed when the working directory was removed.
}

@@ -46,2 +58,5 @@ // Maps preserve insertion order, so we iterate in reverse, starting at the

if (!isAbsolute(filename) && basePath === undefined) {
continue
}
const filesToLinesOutput = filesToLines[filename]

@@ -61,4 +76,9 @@ if (!filesToLinesOutput) {

for (const file of files) {
const cacheKey = makeSourceCacheKey(file, basePath)
if (cacheKey === undefined) {
continue
}
// If we failed to read this before, dont try reading it again.
if (LRU_FILE_CONTENTS_FS_READ_FAILED.get(file)) {
if (LRU_FILE_CONTENTS_FS_READ_FAILED.get(cacheKey)) {
continue

@@ -76,8 +96,8 @@ }

const ranges = makeLineReaderRanges(filesToLineRanges)
if (ranges.every((r) => rangeExistsInContentCache(file, r))) {
if (ranges.every((r) => rangeExistsInContentCache(cacheKey, r))) {
continue
}
const cache = emplace(LRU_FILE_CONTENTS_CACHE, file, {})
readlinePromises.push(getContextLinesFromFile(file, ranges, cache))
const cache = emplace(LRU_FILE_CONTENTS_CACHE, cacheKey, {})
readlinePromises.push(getContextLinesFromFile(file, ranges, cache, cacheKey, openSourceFile, logger))
}

@@ -91,3 +111,3 @@

if (frames && frames.length > 0) {
addSourceContextToFrames(frames, LRU_FILE_CONTENTS_CACHE)
addSourceContextToFrames(frames, LRU_FILE_CONTENTS_CACHE, basePath)
}

@@ -103,20 +123,94 @@

/**
* Opens a bounded regular file without blocking on special files such as FIFOs.
* File validation and content reads use the same descriptor so path replacement cannot change what is read.
*/
async function openRegularSourceFile(
path: string,
openSourceFile: OpenSourceFile,
logger?: Pick<Logger, 'debug'>
): Promise<FileHandle | undefined> {
let fileHandle: FileHandle | undefined
let isValid = false
try {
fileHandle = await openSourceFile(path, constants.O_RDONLY | constants.O_NONBLOCK)
const fileStat = await fileHandle.stat()
if (!fileStat.isFile()) {
return undefined
}
if (fileStat.size > MAX_CONTEXTLINES_FILE_SIZE) {
logger?.debug(
`Skipping source context for oversized file ${path}: ${fileStat.size} bytes exceeds ${MAX_CONTEXTLINES_FILE_SIZE}`
)
return undefined
}
isValid = true
return fileHandle
} catch {
return undefined
} finally {
if (fileHandle && !isValid) {
await fileHandle.close().catch(() => {})
}
}
}
/**
* Extracts lines from a file and stores them in a cache.
*/
function getContextLinesFromFile(path: string, ranges: ReadlineRange[], output: Record<number, string>): Promise<void> {
async function getContextLinesFromFile(
path: string,
ranges: ReadlineRange[],
output: Record<number, string>,
cacheKey: string,
openSourceFile: OpenSourceFile,
logger?: Pick<Logger, 'debug'>
): Promise<void> {
const fileHandle = await openRegularSourceFile(path, openSourceFile, logger)
if (fileHandle === undefined) {
LRU_FILE_CONTENTS_FS_READ_FAILED.set(cacheKey, 1)
return
}
const openedFileHandle = fileHandle
return new Promise((resolve) => {
let finished = false
// We need to explicitly destroy the stream and close its descriptor to prevent memory leaks,
// removing the listeners on the readline interface is not enough.
// Otherwise, repeated exception captures can keep opening the same files without closing them.
function destroyStreamAndResolve(stream?: ReadStream): void {
if (finished) {
return
}
finished = true
stream?.destroy()
void openedFileHandle.close().then(resolve, resolve)
}
// It is important *not* to have any async code between createInterface and the 'line' event listener
// as it will cause the 'line' event to
// be emitted before the listener is attached.
const stream = createReadStream(path)
const lineReaded = createInterface({
input: stream,
})
let stream: ReadStream
try {
stream = openedFileHandle.createReadStream({
autoClose: false,
start: 0,
end: MAX_CONTEXTLINES_FILE_SIZE - 1,
})
} catch {
LRU_FILE_CONTENTS_FS_READ_FAILED.set(cacheKey, 1)
destroyStreamAndResolve()
return
}
// We need to explicitly destroy the stream to prevent memory leaks,
// removing the listeners on the readline interface is not enough.
// Otherwise, repeated exception captures can keep opening the same files without closing them.
function destroyStreamAndResolve(): void {
stream.destroy()
resolve()
let lineReaded: ReturnType<typeof createInterface>
try {
lineReaded = createInterface({
input: stream,
})
} catch {
LRU_FILE_CONTENTS_FS_READ_FAILED.set(cacheKey, 1)
destroyStreamAndResolve(stream)
return
}

@@ -130,3 +224,3 @@

// We should never reach this point, but if we do, we should resolve the promise to prevent it from hanging.
destroyStreamAndResolve()
destroyStreamAndResolve(stream)
return

@@ -140,7 +234,7 @@ }

function onStreamError(): void {
// Mark file path as failed to read and prevent multiple read attempts.
LRU_FILE_CONTENTS_FS_READ_FAILED.set(path, 1)
// Mark the file as failed to prevent repeated read attempts.
LRU_FILE_CONTENTS_FS_READ_FAILED.set(cacheKey, 1)
lineReaded.close()
lineReaded.removeAllListeners()
destroyStreamAndResolve()
destroyStreamAndResolve(stream)
}

@@ -152,3 +246,3 @@

lineReaded.on('error', onStreamError)
lineReaded.on('close', destroyStreamAndResolve)
lineReaded.on('close', () => destroyStreamAndResolve(stream))

@@ -189,3 +283,4 @@ lineReaded.on('line', (line) => {

frames: CoreErrorTracking.StackFrame[],
cache: CoreErrorTracking.ReduceableCache<string, Record<number, string>>
cache: CoreErrorTracking.ReduceableCache<string, Record<number, string>>,
basePath: string | undefined
): void {

@@ -195,3 +290,4 @@ for (const frame of frames) {

if (frame.filename && frame.context_line === undefined && typeof frame.lineno === 'number') {
const contents = cache.get(frame.filename)
const cacheKey = makeSourceCacheKey(frame.filename, basePath)
const contents = cacheKey === undefined ? undefined : cache.get(cacheKey)
if (contents === undefined) {

@@ -296,7 +392,14 @@ continue

function makeSourceCacheKey(path: string, basePath: string | undefined): string | undefined {
if (isAbsolute(path)) {
return JSON.stringify([null, path])
}
return basePath === undefined ? undefined : JSON.stringify([basePath, path])
}
/**
* Checks if we have all the contents that we need in the cache.
*/
function rangeExistsInContentCache(file: string, range: ReadlineRange): boolean {
const contents = LRU_FILE_CONTENTS_CACHE.get(file)
function rangeExistsInContentCache(cacheKey: string, range: ReadlineRange): boolean {
const contents = LRU_FILE_CONTENTS_CACHE.get(cacheKey)
if (contents === undefined) {

@@ -303,0 +406,0 @@ return false

@@ -1,1 +0,1 @@

export const version = '5.47.6'
export const version = '5.47.7'