fix(mount): survive large and corrupt files when mounting collections (#8514)

This commit is contained in:
Chirag Chandrashekhar
2026-07-08 13:08:55 +05:30
committed by GitHub
parent 7af6150f42
commit da707ea0d5
4 changed files with 54 additions and 5 deletions

View File

@@ -189,7 +189,7 @@ class MountManager {
const result = parsed.get(e.relativePath);
if (!result) continue;
if (result.error) {
entry.state.set(e.relativePath, { error: result.error });
entry.state.set(e.relativePath, { data: result.data, error: result.error });
continue;
}
entry.state.set(e.relativePath, { data: result.data });

View File

@@ -182,6 +182,9 @@ const buildTree = (collectionPath, parserResults, options = {}) => {
hydrateRequestUuids(data, null, collectionPath);
tree.root = data;
}
if (tree.root && typeof tree.root === 'object') {
tree.root.pathname = path.join(collectionPath, relativePath);
}
}
} else if (cls.type === 'folder') {
folderRoots.set(path.dirname(relativePath), entry);

View File

@@ -15,7 +15,8 @@ class Pool {
const workers = Math.max(1, size ?? os.availableParallelism());
this.#pool = workerpool.pool(WORKER_FILE, {
maxWorkers: workers,
workerType: 'thread'
workerType: 'thread',
workerThreadOpts: { resourceLimits: { maxOldGenerationSizeMb: 512 } }
});
}

View File

@@ -5,11 +5,49 @@ const filestore = require('@usebruno/filestore');
const sha256 = (buf) => crypto.createHash('sha256').update(buf).digest('hex');
const parseContent = (content, format, type) => {
const REDACT_BODY_THRESHOLD = 512 * 1024;
const parseLargeBruRequest = (content, format) => {
const { bruFileStringWithRedactedBody, extractedBodyContent } = filestore.parseRequestAndRedactBody(content, { format });
const data = filestore.parseRequest(bruFileStringWithRedactedBody, { format });
data.request = data.request || {};
data.request.body = data.request.body || {};
const body = extractedBodyContent || {};
if (body.json) data.request.body.json = body.json;
if (body.text) data.request.body.text = body.text;
if (body.xml) data.request.body.xml = body.xml;
if (body.sparql) data.request.body.sparql = body.sparql;
if (body.graphql) {
data.request.body.graphql = data.request.body.graphql || {};
data.request.body.graphql.query = body.graphql;
}
return data;
};
const extractBruMeta = (content) => {
const data = {};
const match = content.match(/meta\s*\{\s*([\s\S]*?)\s*\}/);
if (match) {
for (const line of match[1].replace(/\r\n/g, '\n').split('\n')) {
const idx = line.indexOf(':');
if (idx === -1) continue;
const key = line.slice(0, idx).trim();
const value = line.slice(idx + 1).trim();
if (!key || !value) continue;
data[key] = Number.isNaN(Number(value)) ? value : Number(value);
}
}
if (data.type === 'http') data.type = 'http-request';
else if (data.type === 'graphql') data.type = 'graphql-request';
return data;
};
const parseContent = (content, format, type, byteSize) => {
if (type === 'config' || format === 'json') return JSON.parse(content);
const options = { format };
switch (type) {
case 'request':
if (format === 'bru' && byteSize >= REDACT_BODY_THRESHOLD) return parseLargeBruRequest(content, format);
return filestore.parseRequest(content, options);
case 'collection':
return filestore.parseCollection(content, options);
@@ -26,9 +64,16 @@ const parseFile = ({ collectionPath, relativePath, format, type }) => {
const absolutePath = path.join(collectionPath, relativePath);
const buf = fs.readFileSync(absolutePath);
const stat = fs.statSync(absolutePath, { bigint: true });
const mtime = stat.mtimeNs;
const hash = sha256(buf);
const content = buf.toString('utf8');
const data = parseContent(content, format, type);
return { relativePath, mtime: stat.mtimeNs, hash: sha256(buf), data, format, type };
try {
const data = parseContent(content, format, type, buf.length);
return { relativePath, mtime, hash, data, format, type };
} catch (err) {
const data = format === 'bru' && type === 'request' ? extractBruMeta(content) : {};
return { relativePath, mtime, hash, data, format, type, partial: true, error: { message: err.message, stack: err.stack } };
}
};
module.exports = parseFile;