Improve attachment memory safety, downloads, and high-memory alert UX.
Queue Release Build / prepare (push) Successful in 20s
Deploy Web Apps / deploy (push) Successful in 9m2s
Queue Release Build / build-windows (push) Successful in 28m8s
Queue Release Build / build-linux (push) Successful in 47m26s
Queue Release Build / build-android (push) Successful in 19m52s
Queue Release Build / finalize (push) Successful in 4m42s
Queue Release Build / prepare (push) Successful in 20s
Deploy Web Apps / deploy (push) Successful in 9m2s
Queue Release Build / build-windows (push) Successful in 28m8s
Queue Release Build / build-linux (push) Successful in 47m26s
Queue Release Build / build-android (push) Successful in 19m52s
Queue Release Build / finalize (push) Successful in 4m42s
Stream large receives to disk with chunk acks to cap renderer RAM, evict off-screen display blobs, and route exports through a disk-aware download service. Fix the high-memory dialog (backdrop dismiss, copy, log actions), allow diagnostics paths in the path jail, and restore persisted image hydration after reload. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
import '@angular/compiler';
|
||||
import {
|
||||
afterEach,
|
||||
beforeEach,
|
||||
describe,
|
||||
expect,
|
||||
it,
|
||||
vi
|
||||
} from 'vitest';
|
||||
import { EnvironmentInjector, Injector } from '@angular/core';
|
||||
|
||||
import { PerfDiagnosticsCollector } from './diagnostics.collector';
|
||||
import {
|
||||
bootstrapPerfDiagnostics,
|
||||
registerImmediatePerfDiagCollector,
|
||||
resetDiagnosticsBootstrapStateForTests
|
||||
} from './diagnostics.bootstrap';
|
||||
import type { PerfDiagEntry } from './diagnostics.models';
|
||||
|
||||
describe('diagnostics.bootstrap', () => {
|
||||
let injector: EnvironmentInjector;
|
||||
let collector: {
|
||||
collectSample: ReturnType<typeof vi.fn>;
|
||||
buildEntries: ReturnType<typeof vi.fn>;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
resetDiagnosticsBootstrapStateForTests();
|
||||
|
||||
collector = {
|
||||
collectSample: vi.fn(() => ({
|
||||
storeDomains: { attachment: 1024 },
|
||||
storeBytes: { attachment: 1024 },
|
||||
components: { ChatMessageItemComponent: 12 },
|
||||
componentDomains: { chat: 12 },
|
||||
suspectedLeaks: [],
|
||||
heap: { usedJsHeapMb: 256, totalJsHeapMb: 300 },
|
||||
route: '/room/test',
|
||||
durationMs: 2
|
||||
})),
|
||||
buildEntries: vi.fn(() => ([
|
||||
{
|
||||
collectedAt: 1,
|
||||
source: 'renderer',
|
||||
type: 'store',
|
||||
payload: { domains: { attachment: 1024 } }
|
||||
},
|
||||
{
|
||||
collectedAt: 1,
|
||||
source: 'renderer',
|
||||
type: 'components',
|
||||
payload: { domains: { chat: 12 } }
|
||||
},
|
||||
{
|
||||
collectedAt: 1,
|
||||
source: 'renderer',
|
||||
type: 'heap',
|
||||
payload: { usedJsHeapMb: 256 }
|
||||
}
|
||||
] satisfies PerfDiagEntry[]))
|
||||
};
|
||||
|
||||
injector = Injector.create({
|
||||
providers: [{ provide: PerfDiagnosticsCollector, useValue: collector }]
|
||||
}) as EnvironmentInjector;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetDiagnosticsBootstrapStateForTests();
|
||||
});
|
||||
|
||||
it('registers immediate renderer samples without perf diagnostics being enabled', () => {
|
||||
registerImmediatePerfDiagCollector(injector);
|
||||
|
||||
const entries = globalThis.__collectPerfDiagSample?.() ?? [];
|
||||
|
||||
expect(entries).toHaveLength(3);
|
||||
expect(entries.find((entry) => entry.type === 'store')?.payload).toEqual({
|
||||
domains: { attachment: 1024 }
|
||||
});
|
||||
|
||||
expect(entries.find((entry) => entry.type === 'components')?.payload).toEqual({
|
||||
domains: { chat: 12 }
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps the immediate collector registered after periodic sampling stops', async () => {
|
||||
vi.useFakeTimers();
|
||||
vi.stubGlobal('window', { addEventListener: vi.fn() });
|
||||
|
||||
registerImmediatePerfDiagCollector(injector);
|
||||
|
||||
const reportSample = vi.fn(async () => {
|
||||
throw new Error('writer unavailable');
|
||||
});
|
||||
|
||||
await bootstrapPerfDiagnostics({
|
||||
isPerfDiagEnabled: async () => true,
|
||||
reportPerfDiagSample: reportSample
|
||||
}, injector);
|
||||
|
||||
await vi.runOnlyPendingTimersAsync();
|
||||
|
||||
expect(globalThis.__collectPerfDiagSample?.()).toHaveLength(3);
|
||||
vi.useRealTimers();
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
});
|
||||
@@ -16,11 +16,38 @@ const SAMPLE_INTERVAL_MS = 10_000;
|
||||
|
||||
let started = false;
|
||||
let sampleTimer: ReturnType<typeof setInterval> | null = null;
|
||||
let immediateCollectorRegistered = false;
|
||||
|
||||
export function registerImmediatePerfDiagCollector(injector: EnvironmentInjector): void {
|
||||
if (immediateCollectorRegistered) {
|
||||
return;
|
||||
}
|
||||
|
||||
let immediateSampleCollector: PerfDiagnosticsCollector | null = null;
|
||||
|
||||
runInInjectionContext(injector, () => {
|
||||
immediateSampleCollector = inject(PerfDiagnosticsCollector);
|
||||
});
|
||||
|
||||
globalThis.__collectPerfDiagSample = () => {
|
||||
if (!immediateSampleCollector) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const sample = immediateSampleCollector.collectSample();
|
||||
|
||||
return sample ? immediateSampleCollector.buildEntries(sample) : [];
|
||||
};
|
||||
|
||||
immediateCollectorRegistered = true;
|
||||
}
|
||||
|
||||
export async function bootstrapPerfDiagnostics(
|
||||
api: ElectronApi,
|
||||
injector: EnvironmentInjector
|
||||
): Promise<void> {
|
||||
registerImmediatePerfDiagCollector(injector);
|
||||
|
||||
const reportSample = api.reportPerfDiagSample;
|
||||
|
||||
if (started || !api.isPerfDiagEnabled || !reportSample) {
|
||||
@@ -41,22 +68,6 @@ export async function bootstrapPerfDiagnostics(
|
||||
|
||||
started = true;
|
||||
|
||||
let immediateSampleCollector: PerfDiagnosticsCollector | null = null;
|
||||
|
||||
runInInjectionContext(injector, () => {
|
||||
immediateSampleCollector = inject(PerfDiagnosticsCollector);
|
||||
});
|
||||
|
||||
globalThis.__collectPerfDiagSample = () => {
|
||||
if (!immediateSampleCollector) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const sample = immediateSampleCollector.collectSample();
|
||||
|
||||
return sample ? immediateSampleCollector.buildEntries(sample) : [];
|
||||
};
|
||||
|
||||
const reporter: PerfDiagReporter = {
|
||||
report: (entry: PerfDiagEntry) => reportSample(entry)
|
||||
};
|
||||
@@ -113,6 +124,12 @@ function stopPerfDiagnosticsSampling(): void {
|
||||
sampleTimer = null;
|
||||
}
|
||||
|
||||
delete globalThis.__collectPerfDiagSample;
|
||||
started = false;
|
||||
}
|
||||
|
||||
/** @internal Resets module state between unit tests. */
|
||||
export function resetDiagnosticsBootstrapStateForTests(): void {
|
||||
stopPerfDiagnosticsSampling();
|
||||
immediateCollectorRegistered = false;
|
||||
delete globalThis.__collectPerfDiagSample;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user