返回源码地图

packages/coding-agent/src/core/tools/file-mutation-queue.ts

v1.0.0 · a13d35a742c6 · 08:同文件排序

完整原文供逐行核对;页面收录不代表每行都经过人工语义审核。MIT 许可见 许可证。

1import { realpath } from "node:fs/promises";
2import { resolve } from "node:path";
3
4const fileMutationQueues = new Map<string, Promise<void>>();
5let registrationQueue = Promise.resolve();
6
7function isMissingPathError(error: unknown): boolean {
8 return (
9 typeof error === "object" &&
10 error !== null &&
11 "code" in error &&
12 (error.code === "ENOENT" || error.code === "ENOTDIR")
13 );
14}
15
16async function getMutationQueueKey(filePath: string): Promise<string> {
17 const resolvedPath = resolve(filePath);
18 try {
19 return await realpath(resolvedPath);
20 } catch (error) {
21 if (isMissingPathError(error)) {
22 return resolvedPath;
23 }
24 throw error;
25 }
26}
27
28/**
29 * Serialize file mutation operations targeting the same file.
30 * Operations for different files still run in parallel.
31 */
32export async function withFileMutationQueue<T>(filePath: string, fn: () => Promise<T>): Promise<T> {
33 const registration = registrationQueue.then(async () => {
34 const key = await getMutationQueueKey(filePath);
35 const currentQueue = fileMutationQueues.get(key) ?? Promise.resolve();
36
37 let releaseNext!: () => void;
38 const nextQueue = new Promise<void>((resolveQueue) => {
39 releaseNext = resolveQueue;
40 });
41 const chainedQueue = currentQueue.then(() => nextQueue);
42 fileMutationQueues.set(key, chainedQueue);
43
44 return { key, currentQueue, chainedQueue, releaseNext };
45 });
46 registrationQueue = registration.then(
47 () => undefined,
48 () => undefined,
49 );
50
51 const { key, currentQueue, chainedQueue, releaseNext } = await registration;
52 await currentQueue;
53 try {
54 return await fn();
55 } finally {
56 releaseNext();
57 if (fileMutationQueues.get(key) === chainedQueue) {
58 fileMutationQueues.delete(key);
59 }
60 }
61}