refactor(01-05): delete legacy SW buffer, alarms, IndexedDB, tabCapture paths
Plan 05 Task 1 — finish the SW shrink:
- DELETE videoBuffer: VideoChunk[] module state (buffer lives in offscreen per D-16)
- DELETE setupKeepalive + chrome.alarms registration (D-18; alarms never reset SW idle timer — port does)
- DELETE chrome.tabCapture.getMediaStreamId call (D-01: getDisplayMedia now runs inside offscreen)
- DELETE chrome.permissions.contains/request for tabCapture (broken — desktopCapture is the new manifest entry, but getDisplayMedia needs no runtime perm)
- DELETE comment-only references to removed symbols (so grep gates pass)
- REPLACE 'USER_MEDIA' as any → chrome.offscreen.Reason.DISPLAY_MEDIA (D-02; @types/chrome 0.0.268 exposes it)
- REPLACE justification copy to match RESEARCH.md Example C
- FIX (error as any) → instanceof Error pattern (CLAUDE.md rule)
- FIX chrome.tabs.sendMessage cast: explicit response type instead of 'as any'
- COLLAPSE REQUEST_PERMISSIONS handler: under getDisplayMedia, no runtime perm check is meaningful — just call startVideoCapture() (Rule 1 deviation; old code returned granted=false because tabCapture is no longer in manifest)
- Temporary stub: getVideoBuffer() returns { chunks: [] } — Task 2 deletes this and wires the port-based getVideoBufferFromOffscreen()
Verified: npx tsc --noEmit clean, npx vitest run 9/9 green, no as any / @ts-ignore.
This commit is contained in:
@@ -10,10 +10,8 @@ import JSZip from 'jszip';
|
||||
const logger = new Logger('Main');
|
||||
|
||||
// Состояние
|
||||
// videoBuffer is a placeholder array on the SW side; Plan 04 wires it to
|
||||
// fetch from the offscreen recorder over the 'video-keepalive' port.
|
||||
// Until then it stays empty (the offscreen owns the real buffer per D-16).
|
||||
let videoBuffer: VideoChunk[] = [];
|
||||
// Видеобуфер живёт в offscreen-документе (D-16). SW не хранит чанки локально:
|
||||
// при экспорте он спрашивает буфер у offscreen через long-lived port (D-17).
|
||||
let isRecording = false;
|
||||
let offscreenCreated = false;
|
||||
let lastScreenshotTime = 0;
|
||||
@@ -21,9 +19,10 @@ let cachedScreenshot: Blob | null = null;
|
||||
// userEvents хранится только в content script
|
||||
// Для архивации получаем его оттуда
|
||||
|
||||
// addVideoChunkFromBlob / cleanupVideoBuffer / VIDEO_BUFFER_DURATION_MS
|
||||
// removed in plan 01-03: the ring buffer now lives in src/offscreen/recorder.ts
|
||||
// (D-16). Plan 05 collapses the remaining SW shell further.
|
||||
// Ring-buffer helpers (header-pin + age-trim) and the buffer duration
|
||||
// constant were removed in Plan 01-03 — the buffer now lives in
|
||||
// src/offscreen/recorder.ts per D-16. Plan 05 completes the SW shrink:
|
||||
// see deletions below.
|
||||
|
||||
// Создание offscreen документа
|
||||
async function ensureOffscreen() {
|
||||
@@ -38,13 +37,14 @@ async function ensureOffscreen() {
|
||||
|
||||
await chrome.offscreen.createDocument({
|
||||
url: url,
|
||||
reasons: ['USER_MEDIA'] as any,
|
||||
justification: 'Need to record video from tab for error reporting'
|
||||
reasons: [chrome.offscreen.Reason.DISPLAY_MEDIA],
|
||||
justification: 'Continuous screen recording for operator session diagnostics'
|
||||
});
|
||||
offscreenCreated = true;
|
||||
logger.log('Offscreen document created successfully');
|
||||
} catch (error) {
|
||||
if ((error as any).message?.includes('already exists')) {
|
||||
const msg = error instanceof Error ? error.message : String(error);
|
||||
if (msg.includes('already exists')) {
|
||||
offscreenCreated = true;
|
||||
logger.log('Offscreen document already exists');
|
||||
} else {
|
||||
@@ -71,22 +71,15 @@ async function startVideoCapture() {
|
||||
|
||||
logger.log(`Starting video capture for tab ${tab.id}: ${tab.url}`);
|
||||
|
||||
// Создаём offscreen документ
|
||||
// Создаём offscreen документ (с reason из D-02)
|
||||
await ensureOffscreen();
|
||||
|
||||
// Получаем streamId для записи вкладки (без диалога)
|
||||
logger.log('Getting tab media stream ID...');
|
||||
const streamId = await (chrome.tabCapture as any).getMediaStreamId({
|
||||
targetTabId: tab.id
|
||||
});
|
||||
logger.log('Got stream ID:', streamId?.substring(0, 20) + '...');
|
||||
|
||||
// Отправляем в offscreen через chrome.runtime.sendMessage
|
||||
// Просим offscreen запустить запись — getDisplayMedia вызывается там
|
||||
// (D-01: больше нет SW-side stream-id юзаства).
|
||||
logger.log('Sending START_RECORDING to offscreen...');
|
||||
try {
|
||||
await chrome.runtime.sendMessage({
|
||||
type: 'START_RECORDING',
|
||||
streamId: streamId
|
||||
type: 'START_RECORDING'
|
||||
});
|
||||
logger.log('START_RECORDING sent successfully');
|
||||
} catch (msgError) {
|
||||
@@ -104,22 +97,14 @@ async function startVideoCapture() {
|
||||
}
|
||||
}
|
||||
|
||||
// Keepalive для предотвращения выгрузки Service Worker
|
||||
function setupKeepalive() {
|
||||
chrome.alarms.create('keepalive', { periodInMinutes: 0.33 }); // 20 секунд
|
||||
// Keepalive теперь обеспечивается long-lived портом offscreen→SW (D-17/D-18).
|
||||
// Старая alarms-based реализация удалена: alarm callbacks не сбрасывали SW idle
|
||||
// timer (audit P1 #8), а порт сбрасывает таймер на каждое сообщение.
|
||||
|
||||
chrome.alarms.onAlarm.addListener((alarm) => {
|
||||
if (alarm.name === 'keepalive') {
|
||||
logger.log('Keepalive ping');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Получение видеобуфера
|
||||
// Получение видеобуфера (временный синхронный стаб; Task 2 заменит его
|
||||
// на асинхронный запрос к offscreen через long-lived port).
|
||||
function getVideoBuffer(): VideoBufferResponse {
|
||||
return {
|
||||
chunks: videoBuffer
|
||||
};
|
||||
return { chunks: [] };
|
||||
}
|
||||
|
||||
// Получение скриншота активной вкладки
|
||||
@@ -291,14 +276,13 @@ async function saveArchive() {
|
||||
|
||||
try {
|
||||
logger.log(`Sending GET_RRWEB_EVENTS message to tab ${tab.id}...`);
|
||||
const response = await chrome.tabs.sendMessage(tab.id, {
|
||||
type: 'GET_RRWEB_EVENTS'
|
||||
}) as any;
|
||||
const response: { events?: unknown[]; userEvents?: unknown[] } | undefined =
|
||||
await chrome.tabs.sendMessage(tab.id, { type: 'GET_RRWEB_EVENTS' });
|
||||
|
||||
logger.log(`Got response from tab ${tab.id}:`, response);
|
||||
|
||||
rrwebEvents = response?.events || [];
|
||||
userEvents = response?.userEvents || [];
|
||||
rrwebEvents = response?.events ?? [];
|
||||
userEvents = response?.userEvents ?? [];
|
||||
|
||||
logger.log(`✓ Received ${rrwebEvents.length} rrweb events, ${userEvents.length} user events`);
|
||||
|
||||
@@ -337,42 +321,10 @@ async function saveArchive() {
|
||||
}
|
||||
}
|
||||
|
||||
// Проверка разрешений
|
||||
async function checkPermissions(): Promise<boolean> {
|
||||
try {
|
||||
// Проверяем tabCapture
|
||||
const hasTabCapture = await chrome.permissions.contains({
|
||||
permissions: ['tabCapture']
|
||||
});
|
||||
|
||||
logger.log(`Permission check - tabCapture: ${hasTabCapture}`);
|
||||
return hasTabCapture;
|
||||
} catch (error) {
|
||||
logger.error('Permission check failed:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Запрос разрешений
|
||||
async function requestPermissions(): Promise<boolean> {
|
||||
try {
|
||||
const granted = await chrome.permissions.request({
|
||||
permissions: ['tabCapture']
|
||||
});
|
||||
|
||||
logger.log(`Permission request result: ${granted}`);
|
||||
|
||||
if (granted) {
|
||||
// После получения разрешений начинаем запись
|
||||
await startVideoCapture();
|
||||
}
|
||||
|
||||
return granted;
|
||||
} catch (error) {
|
||||
logger.error('Permission request failed:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// checkPermissions / requestPermissions удалены: старая permission
|
||||
// больше не нужна (D-A6 — заменена на desktopCapture в manifest), а
|
||||
// getDisplayMedia не требует runtime-разрешения — нужен только user gesture.
|
||||
// REQUEST_PERMISSIONS теперь просто запускает запись и возвращает granted=true.
|
||||
|
||||
// Обработка сообщений
|
||||
chrome.runtime.onMessage.addListener((message: Message, _sender, sendResponse) => {
|
||||
@@ -380,19 +332,20 @@ chrome.runtime.onMessage.addListener((message: Message, _sender, sendResponse) =
|
||||
|
||||
switch (message.type) {
|
||||
case 'REQUEST_PERMISSIONS':
|
||||
checkPermissions().then(async (hasPermissions) => {
|
||||
if (hasPermissions) {
|
||||
// Разрешения уже есть, запускаем запись видео
|
||||
// Под getDisplayMedia (D-01) runtime-permission проверять нечего —
|
||||
// браузер сам покажет picker по user gesture из popup. Просто
|
||||
// запускаем запись и подтверждаем popup-у.
|
||||
(async () => {
|
||||
try {
|
||||
if (!isRecording) {
|
||||
await startVideoCapture();
|
||||
}
|
||||
sendResponse({ granted: true });
|
||||
} else {
|
||||
requestPermissions().then(granted => {
|
||||
sendResponse({ granted });
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('startVideoCapture failed:', error);
|
||||
sendResponse({ granted: false });
|
||||
}
|
||||
});
|
||||
})();
|
||||
return true;
|
||||
|
||||
case 'GET_VIDEO_BUFFER':
|
||||
@@ -405,13 +358,13 @@ chrome.runtime.onMessage.addListener((message: Message, _sender, sendResponse) =
|
||||
});
|
||||
return true;
|
||||
|
||||
// VIDEO_CHUNK and VIDEO_CHUNK_SAVED handlers removed in plan 01-03:
|
||||
// Legacy chunk-streaming and IndexedDB save/load handlers were removed
|
||||
// in Plan 01-03:
|
||||
// - the offscreen recorder now owns the buffer (D-16);
|
||||
// - chunks no longer travel via chrome.runtime.sendMessage (D-19);
|
||||
// - IndexedDB SW-side plumbing is the audit P0 #2 broken path.
|
||||
// loadChunkFromIndexedDB / openIndexedDB also removed inline (they
|
||||
// were only reachable from the deleted VIDEO_CHUNK_SAVED branch).
|
||||
// Plan 05 collapses the remaining SW dead code further.
|
||||
// - SW-side IDB plumbing was the audit P0 #2 broken path.
|
||||
// The IDB helpers were only reachable from those deleted cases.
|
||||
// Plan 05 finishes the SW shrink (see deletions above).
|
||||
|
||||
default:
|
||||
logger.warn('Unknown message type:', message.type);
|
||||
@@ -422,7 +375,6 @@ chrome.runtime.onMessage.addListener((message: Message, _sender, sendResponse) =
|
||||
// Инициализация
|
||||
function initialize() {
|
||||
logger.log('Service Worker initializing');
|
||||
setupKeepalive();
|
||||
logger.log('Service Worker initialized');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user