

























@@ -53,6 +53,26 @@ type RegistryFile = {
5353entries: RegistryEntryPayload[];
5454};
555556+type LegacyRegistryKind = "containers" | "browsers";
57+58+type LegacyRegistryTarget = {
59+kind: LegacyRegistryKind;
60+registryPath: string;
61+shardedDir: string;
62+};
63+64+export type LegacySandboxRegistryInspection = LegacyRegistryTarget & {
65+exists: boolean;
66+valid: boolean;
67+entries: number;
68+};
69+70+export type LegacySandboxRegistryMigrationResult = LegacyRegistryTarget & {
71+status: "missing" | "migrated" | "removed-empty" | "quarantined-invalid";
72+entries: number;
73+quarantinePath?: string;
74+};
75+5676const RegistryEntrySchema = z
5777.object({
5878containerName: z.string(),
@@ -103,7 +123,6 @@ async function readLegacyRegistryFile(registryPath: string): Promise<RegistryFil
103123}
104124105125export async function readRegistry(): Promise<SandboxRegistry> {
106-await migrateMonolithicIfNeeded(SANDBOX_REGISTRY_PATH, SANDBOX_CONTAINERS_DIR);
107126const entries = await readShardedEntries<SandboxRegistryEntry>(SANDBOX_CONTAINERS_DIR);
108127return {
109128entries: entries.map((entry) => normalizeSandboxRegistryEntry(entry)),
@@ -197,57 +216,115 @@ async function readShardedEntries<T extends RegistryEntry>(dir: string): Promise
197216);
198217}
199218200-async function quarantineLegacyRegistry(registryPath: string): Promise<void> {
219+async function quarantineLegacyRegistry(registryPath: string): Promise<string> {
201220const quarantinePath = `${registryPath}.invalid-${Date.now()}`;
202221await fs.rename(registryPath, quarantinePath).catch(async (error) => {
203222const code = (error as { code?: string } | null)?.code;
204223if (code !== "ENOENT") {
205224await fs.rm(registryPath, { force: true });
206225}
207226});
227+return quarantinePath;
208228}
209229210-async function migrateMonolithicIfNeeded(registryPath: string, shardedDir: string): Promise<void> {
230+async function migrateMonolithicIfNeeded(
231+target: LegacyRegistryTarget,
232+): Promise<LegacySandboxRegistryMigrationResult> {
233+const { registryPath, shardedDir } = target;
211234try {
212235await fs.access(registryPath);
213236} catch (error) {
214237const code = (error as { code?: string } | null)?.code;
215238if (code === "ENOENT") {
216-return;
239+return { ...target, status: "missing", entries: 0 };
217240}
218241throw error;
219242}
220243221-await withRegistryLock(registryPath, async () => {
244+return await withRegistryLock(registryPath, async () => {
222245const registry = await readLegacyRegistryFile(registryPath);
223246if (!registry) {
224-await quarantineLegacyRegistry(registryPath);
225-return;
247+const quarantinePath = await quarantineLegacyRegistry(registryPath);
248+return { ...target, status: "quarantined-invalid", entries: 0, quarantinePath };
226249}
227250if (registry.entries.length === 0) {
228251await fs.rm(registryPath, { force: true });
229-return;
252+return { ...target, status: "removed-empty", entries: 0 };
230253}
231254await fs.mkdir(shardedDir, { recursive: true });
232255for (const entry of registry.entries) {
233256await withEntryLock(shardedDir, entry.containerName, async () => {
234-await writeShardedEntry(shardedDir, entry);
257+const existing = await readShardedEntry(shardedDir, entry.containerName);
258+if (!existing) {
259+await writeShardedEntry(shardedDir, entry);
260+}
235261});
236262}
237263await fs.rm(registryPath, { force: true });
264+return { ...target, status: "migrated", entries: registry.entries.length };
238265});
239266}
240267268+function legacyRegistryTargets(): LegacyRegistryTarget[] {
269+return [
270+{
271+kind: "containers",
272+registryPath: SANDBOX_REGISTRY_PATH,
273+shardedDir: SANDBOX_CONTAINERS_DIR,
274+},
275+{
276+kind: "browsers",
277+registryPath: SANDBOX_BROWSER_REGISTRY_PATH,
278+shardedDir: SANDBOX_BROWSERS_DIR,
279+},
280+];
281+}
282+283+export async function inspectLegacySandboxRegistryFiles(): Promise<
284+LegacySandboxRegistryInspection[]
285+> {
286+const inspections: LegacySandboxRegistryInspection[] = [];
287+for (const target of legacyRegistryTargets()) {
288+try {
289+await fs.access(target.registryPath);
290+} catch (error) {
291+const code = (error as { code?: string } | null)?.code;
292+if (code === "ENOENT") {
293+inspections.push({ ...target, exists: false, valid: true, entries: 0 });
294+continue;
295+}
296+throw error;
297+}
298+299+const registry = await readLegacyRegistryFile(target.registryPath);
300+inspections.push({
301+ ...target,
302+exists: true,
303+valid: Boolean(registry),
304+entries: registry?.entries.length ?? 0,
305+});
306+}
307+return inspections;
308+}
309+310+export async function migrateLegacySandboxRegistryFiles(): Promise<
311+LegacySandboxRegistryMigrationResult[]
312+> {
313+const results: LegacySandboxRegistryMigrationResult[] = [];
314+for (const target of legacyRegistryTargets()) {
315+results.push(await migrateMonolithicIfNeeded(target));
316+}
317+return results;
318+}
319+241320export async function readRegistryEntry(
242321containerName: string,
243322): Promise<SandboxRegistryEntry | null> {
244-await migrateMonolithicIfNeeded(SANDBOX_REGISTRY_PATH, SANDBOX_CONTAINERS_DIR);
245323const entry = await readShardedEntry<SandboxRegistryEntry>(SANDBOX_CONTAINERS_DIR, containerName);
246324return entry ? normalizeSandboxRegistryEntry(entry) : null;
247325}
248326249327export async function updateRegistry(entry: SandboxRegistryEntry) {
250-await migrateMonolithicIfNeeded(SANDBOX_REGISTRY_PATH, SANDBOX_CONTAINERS_DIR);
251328await withEntryLock(SANDBOX_CONTAINERS_DIR, entry.containerName, async () => {
252329const existing = await readShardedEntry<SandboxRegistryEntry>(
253330SANDBOX_CONTAINERS_DIR,
@@ -266,19 +343,16 @@ export async function updateRegistry(entry: SandboxRegistryEntry) {
266343}
267344268345export async function removeRegistryEntry(containerName: string) {
269-await migrateMonolithicIfNeeded(SANDBOX_REGISTRY_PATH, SANDBOX_CONTAINERS_DIR);
270346await withEntryLock(SANDBOX_CONTAINERS_DIR, containerName, async () => {
271347await removeShardedEntry(SANDBOX_CONTAINERS_DIR, containerName);
272348});
273349}
274350275351export async function readBrowserRegistry(): Promise<SandboxBrowserRegistry> {
276-await migrateMonolithicIfNeeded(SANDBOX_BROWSER_REGISTRY_PATH, SANDBOX_BROWSERS_DIR);
277352return { entries: await readShardedEntries<SandboxBrowserRegistryEntry>(SANDBOX_BROWSERS_DIR) };
278353}
279354280355export async function updateBrowserRegistry(entry: SandboxBrowserRegistryEntry) {
281-await migrateMonolithicIfNeeded(SANDBOX_BROWSER_REGISTRY_PATH, SANDBOX_BROWSERS_DIR);
282356await withEntryLock(SANDBOX_BROWSERS_DIR, entry.containerName, async () => {
283357const existing = await readShardedEntry<SandboxBrowserRegistryEntry>(
284358SANDBOX_BROWSERS_DIR,
@@ -294,7 +368,6 @@ export async function updateBrowserRegistry(entry: SandboxBrowserRegistryEntry)
294368}
295369296370export async function removeBrowserRegistryEntry(containerName: string) {
297-await migrateMonolithicIfNeeded(SANDBOX_BROWSER_REGISTRY_PATH, SANDBOX_BROWSERS_DIR);
298371await withEntryLock(SANDBOX_BROWSERS_DIR, containerName, async () => {
299372await removeShardedEntry(SANDBOX_BROWSERS_DIR, containerName);
300373});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。