





















@@ -124,56 +124,49 @@ function pickMemoRelevantEnv(env: NodeJS.ProcessEnv): Record<string, string> {
124124);
125125}
126126127-function cloneOwnerMaps(owners: PluginMetadataSnapshotOwnerMaps): PluginMetadataSnapshotOwnerMaps {
128-return {
129-channels: new Map(owners.channels),
130-channelConfigs: new Map(owners.channelConfigs),
131-providers: new Map(owners.providers),
132-modelCatalogProviders: new Map(owners.modelCatalogProviders),
133-cliBackends: new Map(owners.cliBackends),
134-setupProviders: new Map(owners.setupProviders),
135-commandAliases: new Map(owners.commandAliases),
136-contracts: new Map(owners.contracts),
137-};
127+function throwReadonlyPluginMetadataMutation(): never {
128+throw new TypeError("Plugin metadata snapshots are immutable");
138129}
139130140-function cloneSnapshotValue<T>(value: T): T {
141-return value && typeof value === "object" ? structuredClone(value) : value;
131+function freezeSnapshotValue<T>(value: T, seen = new WeakSet<object>()): T {
132+if (!value || typeof value !== "object") {
133+return value;
134+}
135+if (seen.has(value)) {
136+return value;
137+}
138+seen.add(value);
139+if (value instanceof Map) {
140+for (const [key, entry] of value) {
141+freezeSnapshotValue(key, seen);
142+freezeSnapshotValue(entry, seen);
143+}
144+Object.defineProperties(value, {
145+clear: { value: throwReadonlyPluginMetadataMutation },
146+delete: { value: throwReadonlyPluginMetadataMutation },
147+set: { value: throwReadonlyPluginMetadataMutation },
148+});
149+return Object.freeze(value);
150+}
151+if (value instanceof Set) {
152+for (const entry of value) {
153+freezeSnapshotValue(entry, seen);
154+}
155+Object.defineProperties(value, {
156+add: { value: throwReadonlyPluginMetadataMutation },
157+clear: { value: throwReadonlyPluginMetadataMutation },
158+delete: { value: throwReadonlyPluginMetadataMutation },
159+});
160+return Object.freeze(value);
161+}
162+for (const entry of Object.values(value)) {
163+freezeSnapshotValue(entry, seen);
164+}
165+return Object.freeze(value);
142166}
143167144-function clonePluginManifestRecord(plugin: PluginManifestRecord): PluginManifestRecord {
145-return cloneSnapshotValue(plugin);
146-}
147-148-function clonePluginMetadataSnapshot(snapshot: PluginMetadataSnapshot): PluginMetadataSnapshot {
149-const plugins = snapshot.plugins.map(clonePluginManifestRecord);
150-const pluginsById = new Map(plugins.map((plugin) => [plugin.id, plugin]));
151-const diagnostics = snapshot.diagnostics.map(cloneSnapshotValue);
152-return {
153- ...snapshot,
154-index: {
155- ...snapshot.index,
156-installRecords: cloneSnapshotValue(snapshot.index.installRecords ?? {}),
157-plugins: snapshot.index.plugins.map(cloneSnapshotValue),
158-diagnostics: snapshot.index.diagnostics.map(cloneSnapshotValue),
159-},
160-registryDiagnostics: snapshot.registryDiagnostics.map(cloneSnapshotValue),
161-manifestRegistry: {
162- ...snapshot.manifestRegistry,
163- plugins,
164- diagnostics,
165-},
166- plugins,
167- diagnostics,
168-byPluginId: new Map(
169-[...snapshot.byPluginId.entries()].map(([pluginId, plugin]) => [
170-pluginId,
171-pluginsById.get(plugin.id) ?? clonePluginManifestRecord(plugin),
172-]),
173-),
174-owners: cloneOwnerMaps(snapshot.owners),
175-metrics: { ...snapshot.metrics },
176-};
168+function freezePluginMetadataSnapshot(snapshot: PluginMetadataSnapshot): PluginMetadataSnapshot {
169+return freezeSnapshotValue(snapshot);
177170}
178171179172function resolvePersistedRegistryFastMemoFingerprint(params: {
@@ -366,6 +359,10 @@ function indexesMatch(
366359);
367360}
368361362+function cloneSnapshotInput<T>(value: T): T {
363+return value && typeof value === "object" ? structuredClone(value) : value;
364+}
365+369366function normalizeInstalledPluginIndex(index: InstalledPluginIndex): InstalledPluginIndex {
370367return {
371368version: index.version ?? 1,
@@ -374,9 +371,9 @@ function normalizeInstalledPluginIndex(index: InstalledPluginIndex): InstalledPl
374371migrationVersion: index.migrationVersion ?? 1,
375372policyHash: index.policyHash ?? "",
376373generatedAtMs: index.generatedAtMs ?? 0,
377-installRecords: index.installRecords ?? {},
378-plugins: index.plugins ?? [],
379-diagnostics: index.diagnostics ?? [],
374+installRecords: cloneSnapshotInput(index.installRecords ?? {}),
375+plugins: (index.plugins ?? []).map(cloneSnapshotInput),
376+diagnostics: (index.diagnostics ?? []).map(cloneSnapshotInput),
380377 ...(index.warning ? { warning: index.warning } : {}),
381378 ...(index.refreshReason ? { refreshReason: index.refreshReason } : {}),
382379} as InstalledPluginIndex;
@@ -511,20 +508,16 @@ export function loadPluginMetadataSnapshot(
511508const memoKey = computePluginMetadataSnapshotMemoKey({ params, registryState });
512509const memo = findPluginMetadataSnapshotMemo(memoKey);
513510if (memo?.key === memoKey) {
514-return measureDiagnosticsTimelineSpanSync(
515-"plugins.metadata.scan",
516-() => clonePluginMetadataSnapshot(memo.snapshot),
517-{
518-phase: activeTimelineSpan?.phase ?? "startup",
519-config: params.config,
520-env: params.env,
521-attributes: {
522-cacheHit: true,
523-hasWorkspaceDir: params.workspaceDir !== undefined,
524-hasInstalledIndex: params.index !== undefined,
525-},
511+return measureDiagnosticsTimelineSpanSync("plugins.metadata.scan", () => memo.snapshot, {
512+phase: activeTimelineSpan?.phase ?? "startup",
513+config: params.config,
514+env: params.env,
515+attributes: {
516+cacheHit: true,
517+hasWorkspaceDir: params.workspaceDir !== undefined,
518+hasInstalledIndex: params.index !== undefined,
526519},
527-);
520+});
528521}
529522530523const result = measureDiagnosticsTimelineSpanSync(
@@ -540,12 +533,13 @@ export function loadPluginMetadataSnapshot(
540533},
541534},
542535);
536+const snapshot = freezePluginMetadataSnapshot(result.snapshot);
543537if (canMemoizePluginMetadataSnapshotResult(result)) {
544538const cachedRegistryState =
545539result.registrySource === "derived"
546540 ? resolvePersistedRegistryMemoState({
547541 env,
548-index: result.snapshot.index,
542+index: snapshot.index,
549543 ...(params.stateDir ? { stateDir: resolveUserPath(params.stateDir, env) } : {}),
550544 ...(params.preferPersisted !== undefined
551545 ? { preferPersisted: params.preferPersisted }
@@ -555,10 +549,10 @@ export function loadPluginMetadataSnapshot(
555549rememberPluginMetadataSnapshotMemo({
556550key: computePluginMetadataSnapshotMemoKey({ params, registryState: cachedRegistryState }),
557551registryState: cachedRegistryState,
558-snapshot: clonePluginMetadataSnapshot(result.snapshot),
552+ snapshot,
559553});
560554}
561-return result.snapshot;
555+return snapshot;
562556}
563557564558function canMemoizePluginMetadataSnapshotResult(result: {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。