@j0hanz/filesystem-mcp 1.7.1 → 1.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/constants.d.ts +1 -0
- package/dist/lib/constants.js +1 -1
- package/dist/lib/file-operations/common.d.ts +42 -0
- package/dist/lib/file-operations/common.js +87 -0
- package/dist/lib/file-operations/file-info.js +13 -19
- package/dist/lib/file-operations/glob-engine.d.ts +1 -6
- package/dist/lib/file-operations/glob-engine.js +0 -9
- package/dist/lib/file-operations/list-directory.js +19 -39
- package/dist/lib/file-operations/read-multiple-files.js +11 -19
- package/dist/lib/file-operations/search-content.js +106 -113
- package/dist/lib/file-operations/search-files.js +28 -72
- package/dist/lib/file-operations/tree.d.ts +2 -2
- package/dist/lib/file-operations/tree.js +20 -32
- package/dist/prompts.js +3 -3
- package/dist/resources/generated-instructions.js +14 -14
- package/dist/resources/tool-catalog.js +9 -9
- package/dist/resources/tool-info.js +5 -5
- package/dist/resources/workflows.js +17 -17
- package/dist/schemas.js +21 -90
- package/dist/server/bootstrap.js +6 -8
- package/dist/tools/list-directory.js +3 -21
- package/dist/tools/read-multiple.js +21 -19
- package/dist/tools/search-files.js +3 -21
- package/dist/tools/shared.d.ts +3 -0
- package/dist/tools/shared.js +27 -0
- package/dist/tools/stat-many.js +20 -20
- package/dist/tools/task-support.js +7 -5
- package/package.json +1 -1
package/dist/lib/constants.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export declare function parseTrueEnvFlag(value: string | undefined): boolean;
|
|
2
|
+
export declare function parseEnvInt(envVar: string, defaultValue: number, min: number, max: number): number;
|
|
2
3
|
export declare const DEFAULT_LOG_LEVEL: "debug" | "info" | "notice" | "warning" | "error" | "critical" | "alert" | "emergency";
|
|
3
4
|
export declare const REQUIRED_MCP_PROTOCOL_VERSION = "2025-11-25";
|
|
4
5
|
export declare const PARALLEL_CONCURRENCY: number;
|
package/dist/lib/constants.js
CHANGED
|
@@ -12,7 +12,7 @@ function logInvalidEnvValue(envVar, value, expected, defaultValue) {
|
|
|
12
12
|
console.error(`[WARNING] Invalid ${envVar} value: ${value} (must be ${expected}). Using default: ${String(defaultValue)}`);
|
|
13
13
|
}
|
|
14
14
|
// Helper for parsing environment variables (only used for configurable values)
|
|
15
|
-
function parseEnvInt(envVar, defaultValue, min, max) {
|
|
15
|
+
export function parseEnvInt(envVar, defaultValue, min, max) {
|
|
16
16
|
const value = process.env[envVar];
|
|
17
17
|
if (!value)
|
|
18
18
|
return defaultValue;
|
|
@@ -2,3 +2,45 @@ export declare function needsStatsForSort(sortBy: string): boolean;
|
|
|
2
2
|
export declare function withOptionalStoppedReason<T extends object, R extends string>(summary: T, stoppedReason: R | undefined): T | (T & {
|
|
3
3
|
stoppedReason: R;
|
|
4
4
|
});
|
|
5
|
+
export interface DirentLike {
|
|
6
|
+
isDirectory(): boolean;
|
|
7
|
+
isFile(): boolean;
|
|
8
|
+
isSymbolicLink(): boolean;
|
|
9
|
+
}
|
|
10
|
+
export type EntryType = 'file' | 'directory' | 'symlink' | 'other';
|
|
11
|
+
export interface IndexedValue<T> {
|
|
12
|
+
index: number;
|
|
13
|
+
value: T;
|
|
14
|
+
}
|
|
15
|
+
export interface IndexedError {
|
|
16
|
+
index: number;
|
|
17
|
+
error: Error;
|
|
18
|
+
}
|
|
19
|
+
export declare function resolveEntryType(dirent: DirentLike): EntryType;
|
|
20
|
+
export declare function resolveStopReason<R extends string>(options: {
|
|
21
|
+
signal: AbortSignal;
|
|
22
|
+
current: number;
|
|
23
|
+
max: number;
|
|
24
|
+
abortedReason: R;
|
|
25
|
+
maxReason: R;
|
|
26
|
+
}): R | undefined;
|
|
27
|
+
export declare function compareStringValues(left?: string, right?: string): number;
|
|
28
|
+
export declare function compareOptionalNumberDesc(left: number | undefined, right: number | undefined, tieBreak: () => number): number;
|
|
29
|
+
export declare function stableSortByDerivedString<T>(items: T[], derive: (item: T) => string, tieBreak: (left: T, right: T) => number): void;
|
|
30
|
+
export declare function applyIndexedValues<T>(output: T[], results: readonly IndexedValue<T>[]): void;
|
|
31
|
+
export declare function applyIndexedErrors<T>(options: {
|
|
32
|
+
output: T[];
|
|
33
|
+
errors: readonly IndexedError[];
|
|
34
|
+
resolveIndex: (failureIndex: number) => number | undefined;
|
|
35
|
+
buildValue: (resolvedIndex: number, error: Error) => T;
|
|
36
|
+
}): void;
|
|
37
|
+
export interface EntryAccessDependencies {
|
|
38
|
+
normalizePath: (inputPath: string) => string;
|
|
39
|
+
isPathWithinDirectories: (normalizedPath: string, rootDirectories: readonly string[]) => boolean;
|
|
40
|
+
isSensitivePath: (requestedPath: string, resolvedPath: string) => boolean;
|
|
41
|
+
validateSymlinkPath: (inputPath: string, signal: AbortSignal) => Promise<{
|
|
42
|
+
requestedPath: string;
|
|
43
|
+
resolvedPath: string;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
export declare function isEntryAccessibleByType(entryPath: string, entryType: EntryType, rootDirectories: readonly string[], signal: AbortSignal, deps: EntryAccessDependencies): Promise<boolean>;
|
|
@@ -1,9 +1,96 @@
|
|
|
1
1
|
export function needsStatsForSort(sortBy) {
|
|
2
2
|
return sortBy === 'size' || sortBy === 'modified';
|
|
3
3
|
}
|
|
4
|
+
const collator = new Intl.Collator(undefined, { numeric: true });
|
|
4
5
|
export function withOptionalStoppedReason(summary, stoppedReason) {
|
|
5
6
|
if (stoppedReason === undefined) {
|
|
6
7
|
return summary;
|
|
7
8
|
}
|
|
8
9
|
return { ...summary, stoppedReason };
|
|
9
10
|
}
|
|
11
|
+
export function resolveEntryType(dirent) {
|
|
12
|
+
if (dirent.isSymbolicLink())
|
|
13
|
+
return 'symlink';
|
|
14
|
+
if (dirent.isDirectory())
|
|
15
|
+
return 'directory';
|
|
16
|
+
if (dirent.isFile())
|
|
17
|
+
return 'file';
|
|
18
|
+
return 'other';
|
|
19
|
+
}
|
|
20
|
+
export function resolveStopReason(options) {
|
|
21
|
+
if (options.signal.aborted)
|
|
22
|
+
return options.abortedReason;
|
|
23
|
+
if (options.current >= options.max)
|
|
24
|
+
return options.maxReason;
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
export function compareStringValues(left, right) {
|
|
28
|
+
return collator.compare(left ?? '', right ?? '');
|
|
29
|
+
}
|
|
30
|
+
export function compareOptionalNumberDesc(left, right, tieBreak) {
|
|
31
|
+
const diff = (right ?? 0) - (left ?? 0);
|
|
32
|
+
if (diff !== 0)
|
|
33
|
+
return diff;
|
|
34
|
+
return tieBreak();
|
|
35
|
+
}
|
|
36
|
+
export function stableSortByDerivedString(items, derive, tieBreak) {
|
|
37
|
+
const decorated = [];
|
|
38
|
+
for (let index = 0; index < items.length; index += 1) {
|
|
39
|
+
const item = items[index];
|
|
40
|
+
if (item === undefined)
|
|
41
|
+
continue;
|
|
42
|
+
decorated.push({
|
|
43
|
+
item,
|
|
44
|
+
derived: derive(item),
|
|
45
|
+
index,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
decorated.sort((left, right) => {
|
|
49
|
+
const derivedCompare = compareStringValues(left.derived, right.derived);
|
|
50
|
+
if (derivedCompare !== 0)
|
|
51
|
+
return derivedCompare;
|
|
52
|
+
const tiedCompare = tieBreak(left.item, right.item);
|
|
53
|
+
if (tiedCompare !== 0)
|
|
54
|
+
return tiedCompare;
|
|
55
|
+
return left.index - right.index;
|
|
56
|
+
});
|
|
57
|
+
for (let index = 0; index < decorated.length; index += 1) {
|
|
58
|
+
const entry = decorated[index];
|
|
59
|
+
if (!entry)
|
|
60
|
+
continue;
|
|
61
|
+
items[index] = entry.item;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export function applyIndexedValues(output, results) {
|
|
65
|
+
for (const result of results) {
|
|
66
|
+
if (result.index < 0 || result.index >= output.length)
|
|
67
|
+
continue;
|
|
68
|
+
output[result.index] = result.value;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export function applyIndexedErrors(options) {
|
|
72
|
+
for (const failure of options.errors) {
|
|
73
|
+
const resolvedIndex = options.resolveIndex(failure.index);
|
|
74
|
+
if (resolvedIndex === undefined)
|
|
75
|
+
continue;
|
|
76
|
+
if (resolvedIndex < 0 || resolvedIndex >= options.output.length)
|
|
77
|
+
continue;
|
|
78
|
+
options.output[resolvedIndex] = options.buildValue(resolvedIndex, failure.error);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export async function isEntryAccessibleByType(entryPath, entryType, rootDirectories, signal, deps) {
|
|
82
|
+
if (entryType !== 'symlink') {
|
|
83
|
+
const normalizedPath = deps.normalizePath(entryPath);
|
|
84
|
+
if (!deps.isPathWithinDirectories(normalizedPath, rootDirectories)) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
return !deps.isSensitivePath(entryPath, normalizedPath);
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const validated = await deps.validateSymlinkPath(entryPath, signal);
|
|
91
|
+
return !deps.isSensitivePath(validated.requestedPath, validated.resolvedPath);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -5,6 +5,7 @@ import { isAbortError } from '../errors.js';
|
|
|
5
5
|
import { assertNotAborted, getFileType, isHidden, processInParallel, withAbort, } from '../fs-helpers.js';
|
|
6
6
|
import { assertAllowedFileAccess } from '../path-policy.js';
|
|
7
7
|
import { validateExistingPathDetailed } from '../path-validation.js';
|
|
8
|
+
import { applyIndexedErrors, applyIndexedValues } from './common.js';
|
|
8
9
|
const PERM_STRINGS = [
|
|
9
10
|
'---',
|
|
10
11
|
'--x',
|
|
@@ -95,23 +96,6 @@ async function readFileInfoInParallel(paths, options) {
|
|
|
95
96
|
return { index, value };
|
|
96
97
|
}, PARALLEL_CONCURRENCY, options.signal);
|
|
97
98
|
}
|
|
98
|
-
function applyResults(output, results) {
|
|
99
|
-
for (const result of results) {
|
|
100
|
-
output[result.index] = result.value;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
function applyErrors(output, errors, paths) {
|
|
104
|
-
for (const failure of errors) {
|
|
105
|
-
const { index } = failure;
|
|
106
|
-
if (!isValidOutputIndex(index, output.length))
|
|
107
|
-
continue;
|
|
108
|
-
const filePath = paths[index] ?? UNKNOWN_PATH;
|
|
109
|
-
output[index] = { path: filePath, error: failure.error.message };
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
function isValidOutputIndex(index, length) {
|
|
113
|
-
return index >= 0 && index < length;
|
|
114
|
-
}
|
|
115
99
|
function calculateSummary(results) {
|
|
116
100
|
let succeeded = 0;
|
|
117
101
|
let failed = 0;
|
|
@@ -140,8 +124,18 @@ export async function getMultipleFileInfo(paths, options = {}) {
|
|
|
140
124
|
output[index] = { path: paths[index] ?? UNKNOWN_PATH };
|
|
141
125
|
}
|
|
142
126
|
const { results, errors } = await readFileInfoInParallel(paths, options);
|
|
143
|
-
|
|
144
|
-
|
|
127
|
+
applyIndexedValues(output, results);
|
|
128
|
+
applyIndexedErrors({
|
|
129
|
+
output,
|
|
130
|
+
errors,
|
|
131
|
+
resolveIndex: (failureIndex) => failureIndex >= 0 && failureIndex < output.length
|
|
132
|
+
? failureIndex
|
|
133
|
+
: undefined,
|
|
134
|
+
buildValue: (resolvedIndex, error) => ({
|
|
135
|
+
path: paths[resolvedIndex] ?? UNKNOWN_PATH,
|
|
136
|
+
error: error.message,
|
|
137
|
+
}),
|
|
138
|
+
});
|
|
145
139
|
return {
|
|
146
140
|
results: output,
|
|
147
141
|
summary: calculateSummary(output),
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import type { Stats } from 'node:fs';
|
|
2
|
-
|
|
3
|
-
isDirectory(): boolean;
|
|
4
|
-
isFile(): boolean;
|
|
5
|
-
isSymbolicLink(): boolean;
|
|
6
|
-
}
|
|
7
|
-
export declare function resolveEntryType(dirent: DirentLike): 'file' | 'directory' | 'symlink' | 'other';
|
|
2
|
+
import type { DirentLike } from './common.js';
|
|
8
3
|
interface GlobEntry {
|
|
9
4
|
path: string;
|
|
10
5
|
relativePath?: string;
|
|
@@ -4,15 +4,6 @@ import { glob as fsGlob } from 'node:fs/promises';
|
|
|
4
4
|
import { getToolContextSnapshot, publishOpsTraceEnd, publishOpsTraceError, publishOpsTraceStart, shouldPublishOpsTrace, startPerfMeasure, } from '../observability.js';
|
|
5
5
|
import { toPosixPath } from '../path-format.js';
|
|
6
6
|
import { isRecord } from '../type-guards.js';
|
|
7
|
-
export function resolveEntryType(dirent) {
|
|
8
|
-
if (dirent.isDirectory())
|
|
9
|
-
return 'directory';
|
|
10
|
-
if (dirent.isSymbolicLink())
|
|
11
|
-
return 'symlink';
|
|
12
|
-
if (dirent.isFile())
|
|
13
|
-
return 'file';
|
|
14
|
-
return 'other';
|
|
15
|
-
}
|
|
16
7
|
const GLOB_MAGIC_RE = /[*?[\]{}!]/u;
|
|
17
8
|
const DEFAULT_MAX_HIDDEN_DEPTH = 10;
|
|
18
9
|
const GLOB_BATCH_CONCURRENCY = 64;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as fsp from 'node:fs/promises';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import { DEFAULT_LIST_MAX_ENTRIES, DEFAULT_MAX_DEPTH, DEFAULT_SEARCH_TIMEOUT_MS, PARALLEL_CONCURRENCY, } from '../constants.js';
|
|
4
|
-
import { createTimedAbortSignal, processInParallel, withAbort, } from '../fs-helpers.js';
|
|
4
|
+
import { createTimedAbortSignal, isHidden, processInParallel, withAbort, } from '../fs-helpers.js';
|
|
5
5
|
import { isSensitivePath } from '../path-policy.js';
|
|
6
6
|
import { isPathWithinDirectories, normalizePath, validateExistingDirectory, validateExistingPathDetailed, } from '../path-validation.js';
|
|
7
|
-
import { needsStatsForSort, withOptionalStoppedReason } from './common.js';
|
|
8
|
-
import { globEntries
|
|
7
|
+
import { isEntryAccessibleByType, needsStatsForSort, resolveEntryType, resolveStopReason, withOptionalStoppedReason, } from './common.js';
|
|
8
|
+
import { globEntries } from './glob-engine.js';
|
|
9
9
|
function normalizePattern(pattern) {
|
|
10
10
|
if (!pattern || pattern.length === 0)
|
|
11
11
|
return undefined;
|
|
@@ -40,18 +40,11 @@ function resolveMaxDepth(normalized) {
|
|
|
40
40
|
}
|
|
41
41
|
return normalized.maxDepth;
|
|
42
42
|
}
|
|
43
|
-
function getStopReason(signal, acceptedCount, maxEntries) {
|
|
44
|
-
if (signal.aborted)
|
|
45
|
-
return 'aborted';
|
|
46
|
-
if (acceptedCount >= maxEntries)
|
|
47
|
-
return 'maxEntries';
|
|
48
|
-
return undefined;
|
|
49
|
-
}
|
|
50
43
|
async function* readDirectoryEntries(basePath, normalized, needsStats, signal) {
|
|
51
44
|
const dirents = await withAbort(fsp.readdir(basePath, { withFileTypes: true }), signal);
|
|
52
45
|
const entries = [];
|
|
53
46
|
for (const dirent of dirents) {
|
|
54
|
-
if (!normalized.includeHidden && dirent.name
|
|
47
|
+
if (!normalized.includeHidden && isHidden(dirent.name)) {
|
|
55
48
|
continue;
|
|
56
49
|
}
|
|
57
50
|
entries.push({ dirent, entryPath: path.join(basePath, dirent.name) });
|
|
@@ -149,32 +142,6 @@ function trackSymlink(entryType, includeSymlinkTargets, counters) {
|
|
|
149
142
|
counters.symlinksNotFollowed += 1;
|
|
150
143
|
}
|
|
151
144
|
}
|
|
152
|
-
async function isEntryAccessible(entryPath, entryType, basePathDirectories, signal, counters) {
|
|
153
|
-
if (entryType !== 'symlink') {
|
|
154
|
-
const normalized = normalizePath(entryPath);
|
|
155
|
-
if (!isPathWithinDirectories(normalized, basePathDirectories)) {
|
|
156
|
-
counters.skippedInaccessible += 1;
|
|
157
|
-
return false;
|
|
158
|
-
}
|
|
159
|
-
if (isSensitivePath(entryPath, normalized)) {
|
|
160
|
-
counters.skippedInaccessible += 1;
|
|
161
|
-
return false;
|
|
162
|
-
}
|
|
163
|
-
return true;
|
|
164
|
-
}
|
|
165
|
-
try {
|
|
166
|
-
const validated = await validateExistingPathDetailed(entryPath, signal);
|
|
167
|
-
if (isSensitivePath(validated.requestedPath, validated.resolvedPath)) {
|
|
168
|
-
counters.skippedInaccessible += 1;
|
|
169
|
-
return false;
|
|
170
|
-
}
|
|
171
|
-
return true;
|
|
172
|
-
}
|
|
173
|
-
catch {
|
|
174
|
-
counters.skippedInaccessible += 1;
|
|
175
|
-
return false;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
145
|
function appendEntry(entry, entryType, symlinkTarget, ctx) {
|
|
179
146
|
updateTotals(entryType, ctx.totals);
|
|
180
147
|
ctx.entries.push(buildDirectoryEntry(ctx.basePath, entry, entryType, ctx.needsStats, symlinkTarget));
|
|
@@ -214,6 +181,12 @@ async function collectEntries(basePath, normalized, signal, needsStats, maxDepth
|
|
|
214
181
|
const totals = { files: 0, directories: 0 };
|
|
215
182
|
const counters = { skippedInaccessible: 0, symlinksNotFollowed: 0 };
|
|
216
183
|
const basePathDirectories = [basePath];
|
|
184
|
+
const accessDeps = {
|
|
185
|
+
normalizePath,
|
|
186
|
+
isPathWithinDirectories,
|
|
187
|
+
isSensitivePath,
|
|
188
|
+
validateSymlinkPath: validateExistingPathDetailed,
|
|
189
|
+
};
|
|
217
190
|
let truncated = false;
|
|
218
191
|
let stoppedReason;
|
|
219
192
|
const pending = [];
|
|
@@ -232,7 +205,13 @@ async function collectEntries(basePath, normalized, signal, needsStats, maxDepth
|
|
|
232
205
|
entries,
|
|
233
206
|
};
|
|
234
207
|
for await (const entry of stream) {
|
|
235
|
-
const stopReason =
|
|
208
|
+
const stopReason = resolveStopReason({
|
|
209
|
+
signal,
|
|
210
|
+
current: acceptedCount,
|
|
211
|
+
max: normalized.maxEntries,
|
|
212
|
+
abortedReason: 'aborted',
|
|
213
|
+
maxReason: 'maxEntries',
|
|
214
|
+
});
|
|
236
215
|
if (stopReason) {
|
|
237
216
|
truncated = true;
|
|
238
217
|
stoppedReason = stopReason;
|
|
@@ -240,8 +219,9 @@ async function collectEntries(basePath, normalized, signal, needsStats, maxDepth
|
|
|
240
219
|
}
|
|
241
220
|
const entryType = resolveEntryType(entry.dirent);
|
|
242
221
|
trackSymlink(entryType, normalized.includeSymlinkTargets, counters);
|
|
243
|
-
const accessible = await
|
|
222
|
+
const accessible = await isEntryAccessibleByType(entry.path, entryType, basePathDirectories, signal, accessDeps);
|
|
244
223
|
if (!accessible) {
|
|
224
|
+
counters.skippedInaccessible += 1;
|
|
245
225
|
continue;
|
|
246
226
|
}
|
|
247
227
|
acceptedCount += 1;
|
|
@@ -2,6 +2,7 @@ import * as fsp from 'node:fs/promises';
|
|
|
2
2
|
import { DEFAULT_READ_MANY_MAX_TOTAL_SIZE, MAX_TEXT_FILE_SIZE, PARALLEL_CONCURRENCY, } from '../constants.js';
|
|
3
3
|
import { processInParallel, readFile, readFileWithStats, withAbort, } from '../fs-helpers.js';
|
|
4
4
|
import { validateExistingPath } from '../path-validation.js';
|
|
5
|
+
import { applyIndexedErrors, applyIndexedValues } from './common.js';
|
|
5
6
|
const UNKNOWN_PATH = '(unknown)';
|
|
6
7
|
function estimateReadSize(stats, maxSize) {
|
|
7
8
|
// `readFile`/`readFileWithStats` are always invoked with a `maxSize` cap, so the
|
|
@@ -173,11 +174,6 @@ function buildOutput(filePaths) {
|
|
|
173
174
|
}
|
|
174
175
|
return output;
|
|
175
176
|
}
|
|
176
|
-
function applyResults(output, results) {
|
|
177
|
-
for (const result of results) {
|
|
178
|
-
output[result.index] = result.value;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
177
|
function resolveErrorOriginalIndex(failureIndex, filesToProcess, totalInputFiles) {
|
|
182
178
|
// processInParallel implementations vary: some return error indices relative to
|
|
183
179
|
// the submitted batch (filesToProcess), others may forward the task/index.
|
|
@@ -192,18 +188,6 @@ function resolveErrorOriginalIndex(failureIndex, filesToProcess, totalInputFiles
|
|
|
192
188
|
}
|
|
193
189
|
return undefined;
|
|
194
190
|
}
|
|
195
|
-
function applyErrors(output, errors, filesToProcess, filePaths) {
|
|
196
|
-
for (const failure of errors) {
|
|
197
|
-
const originalIndex = resolveErrorOriginalIndex(failure.index, filesToProcess, filePaths.length);
|
|
198
|
-
if (originalIndex === undefined)
|
|
199
|
-
continue;
|
|
200
|
-
const filePath = filePaths[originalIndex] ?? UNKNOWN_PATH;
|
|
201
|
-
output[originalIndex] = {
|
|
202
|
-
path: filePath,
|
|
203
|
-
error: failure.error.message,
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
191
|
function buildFilesToProcess(filePaths, validated, skippedBudget) {
|
|
208
192
|
const filesToProcess = [];
|
|
209
193
|
for (let index = 0; index < filePaths.length; index += 1) {
|
|
@@ -253,8 +237,16 @@ export async function readMultipleFiles(filePaths, options = {}) {
|
|
|
253
237
|
const { skippedBudget, validated } = await collectFileBudget(filePaths, normalized.maxTotalSize, normalized.maxSize, signal);
|
|
254
238
|
const filesToProcess = buildFilesToProcess(filePaths, validated, skippedBudget);
|
|
255
239
|
const { results, errors } = await readFilesInParallel(filesToProcess, normalized, signal, options.onReadComplete);
|
|
256
|
-
|
|
257
|
-
|
|
240
|
+
applyIndexedValues(output, results);
|
|
241
|
+
applyIndexedErrors({
|
|
242
|
+
output,
|
|
243
|
+
errors,
|
|
244
|
+
resolveIndex: (failureIndex) => resolveErrorOriginalIndex(failureIndex, filesToProcess, filePaths.length),
|
|
245
|
+
buildValue: (resolvedIndex, error) => ({
|
|
246
|
+
path: filePaths[resolvedIndex] ?? UNKNOWN_PATH,
|
|
247
|
+
error: error.message,
|
|
248
|
+
}),
|
|
249
|
+
});
|
|
258
250
|
applySkippedBudget(output, skippedBudget, filePaths, normalized.maxTotalSize);
|
|
259
251
|
return output;
|
|
260
252
|
}
|