





















@@ -1,7 +1,5 @@
1-import fs from "node:fs";
2-import os from "node:os";
3-import path from "node:path";
41import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-loader.js";
2+export { movePathToTrash, type MovePathToTrashOptions } from "./browser-trash.js";
5364type CloseTrackedBrowserTabsParams = {
75sessionKeys: Array<string | undefined>;
@@ -14,8 +12,6 @@ type BrowserMaintenanceSurface = {
1412};
15131614let cachedBrowserMaintenanceSurface: BrowserMaintenanceSurface | undefined;
17-const TRASH_DESTINATION_COLLISION_CODES = new Set(["EEXIST", "ENOTEMPTY", "ERR_FS_CP_EEXIST"]);
18-const TRASH_DESTINATION_RETRY_LIMIT = 4;
19152016function hasRequestedSessionKeys(sessionKeys: Array<string | undefined>): boolean {
2117return sessionKeys.some((key) => Boolean(key?.trim()));
@@ -30,125 +26,6 @@ function loadBrowserMaintenanceSurface(): BrowserMaintenanceSurface {
3026return cachedBrowserMaintenanceSurface;
3127}
322833-function getFsErrorCode(error: unknown): string | undefined {
34-if (!error || typeof error !== "object" || !("code" in error)) {
35-return undefined;
36-}
37-const code = (error as NodeJS.ErrnoException).code;
38-return typeof code === "string" ? code : undefined;
39-}
40-41-function isTrashDestinationCollision(error: unknown): boolean {
42-const code = getFsErrorCode(error);
43-return Boolean(code && TRASH_DESTINATION_COLLISION_CODES.has(code));
44-}
45-46-function isSameOrChildPath(candidate: string, parent: string): boolean {
47-return candidate === parent || candidate.startsWith(`${parent}${path.sep}`);
48-}
49-50-function resolveAllowedTrashRoots(): string[] {
51-const roots = [os.homedir(), os.tmpdir()].map((root) => {
52-try {
53-return path.resolve(fs.realpathSync.native(root));
54-} catch {
55-return path.resolve(root);
56-}
57-});
58-return [...new Set(roots)];
59-}
60-61-function assertAllowedTrashTarget(targetPath: string): void {
62-let resolvedTargetPath = path.resolve(targetPath);
63-try {
64-resolvedTargetPath = path.resolve(fs.realpathSync.native(targetPath));
65-} catch {
66-// The subsequent move will surface missing or inaccessible targets.
67-}
68-const isAllowed = resolveAllowedTrashRoots().some(
69-(root) => resolvedTargetPath !== root && isSameOrChildPath(resolvedTargetPath, root),
70-);
71-if (!isAllowed) {
72-throw new Error(`Refusing to trash path outside allowed roots: ${targetPath}`);
73-}
74-}
75-76-function resolveTrashDir(): string {
77-const homeDir = os.homedir();
78-const trashDir = path.join(homeDir, ".Trash");
79-fs.mkdirSync(trashDir, { recursive: true, mode: 0o700 });
80-const trashDirStat = fs.lstatSync(trashDir);
81-if (!trashDirStat.isDirectory() || trashDirStat.isSymbolicLink()) {
82-throw new Error(`Refusing to use non-directory/symlink trash directory: ${trashDir}`);
83-}
84-const realHome = path.resolve(fs.realpathSync.native(homeDir));
85-const resolvedTrashDir = path.resolve(fs.realpathSync.native(trashDir));
86-if (resolvedTrashDir === realHome || !isSameOrChildPath(resolvedTrashDir, realHome)) {
87-throw new Error(`Trash directory escaped home directory: ${trashDir}`);
88-}
89-return resolvedTrashDir;
90-}
91-92-function trashBaseName(targetPath: string): string {
93-const resolvedTargetPath = path.resolve(targetPath);
94-if (resolvedTargetPath === path.parse(resolvedTargetPath).root) {
95-throw new Error(`Refusing to trash root path: ${targetPath}`);
96-}
97-const base = path.basename(resolvedTargetPath).replace(/[\\/]+/g, "");
98-if (!base) {
99-throw new Error(`Unable to derive safe trash basename for: ${targetPath}`);
100-}
101-return base;
102-}
103-104-function resolveContainedPath(root: string, leaf: string): string {
105-const resolvedRoot = path.resolve(root);
106-const resolvedPath = path.resolve(resolvedRoot, leaf);
107-if (!isSameOrChildPath(resolvedPath, resolvedRoot) || resolvedPath === resolvedRoot) {
108-throw new Error(`Trash destination escaped trash directory: ${resolvedPath}`);
109-}
110-return resolvedPath;
111-}
112-113-function reserveTrashDestination(trashDir: string, base: string, timestamp: number): string {
114-const containerPrefix = resolveContainedPath(trashDir, `${base}-${timestamp}-`);
115-const container = fs.mkdtempSync(containerPrefix);
116-const resolvedContainer = path.resolve(container);
117-const resolvedTrashDir = path.resolve(trashDir);
118-if (
119-resolvedContainer === resolvedTrashDir ||
120-!isSameOrChildPath(resolvedContainer, resolvedTrashDir)
121-) {
122-throw new Error(`Trash destination escaped trash directory: ${container}`);
123-}
124-return resolveContainedPath(container, base);
125-}
126-127-function movePathToDestination(targetPath: string, dest: string): boolean {
128-try {
129-fs.renameSync(targetPath, dest);
130-return true;
131-} catch (error) {
132-if (getFsErrorCode(error) !== "EXDEV") {
133-if (isTrashDestinationCollision(error)) {
134-return false;
135-}
136-throw error;
137-}
138-}
139-140-try {
141-fs.cpSync(targetPath, dest, { recursive: true, force: false, errorOnExist: true });
142-fs.rmSync(targetPath, { recursive: true, force: false });
143-return true;
144-} catch (error) {
145-if (isTrashDestinationCollision(error)) {
146-return false;
147-}
148-throw error;
149-}
150-}
151-15229export async function closeTrackedBrowserTabsForSessions(
15330params: CloseTrackedBrowserTabsParams,
15431): Promise<number> {
@@ -165,19 +42,3 @@ export async function closeTrackedBrowserTabsForSessions(
16542}
16643return await surface.closeTrackedBrowserTabsForSessions(params);
16744}
168-169-export async function movePathToTrash(targetPath: string): Promise<string> {
170-// Avoid resolving external trash helpers through the service PATH during cleanup.
171-const base = trashBaseName(targetPath);
172-assertAllowedTrashTarget(targetPath);
173-const trashDir = resolveTrashDir();
174-const timestamp = Date.now();
175-for (let attempt = 0; attempt < TRASH_DESTINATION_RETRY_LIMIT; attempt += 1) {
176-const dest = reserveTrashDestination(trashDir, base, timestamp);
177-if (movePathToDestination(targetPath, dest)) {
178-return dest;
179-}
180-}
181-182-throw new Error(`Unable to choose a unique trash destination for ${targetPath}`);
183-}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。