import {TrustedRuntimeSession} from "./protocol.js";
|
import {collectVisiblePage} from "./page_extract.js";
|
import {NativePortTransport, OwnedTabLifecycle, SlotCoordinator, SlotStateStore} from "./runtime.js";
|
|
const HOST = "com.project_info.bili_dynamic_refresh";
|
const ALARM = "project-info-dynamic-refresh-half-hour";
|
const PERIOD_MINUTES = 30;
|
|
async function hashSlot(value) {
|
const material = new TextEncoder().encode(String(value));
|
const digest = await crypto.subtle.digest("SHA-256", material);
|
return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
}
|
|
function nextBoundary(now = Date.now()) {
|
const interval = PERIOD_MINUTES * 60 * 1000;
|
return Math.floor(now / interval) * interval + interval;
|
}
|
|
function createSession(api) {
|
return new TrustedRuntimeSession(api, {
|
extension_id: chrome.runtime.id,
|
version: chrome.runtime.getManifest().version,
|
manifest_name: chrome.runtime.getManifest().name
|
});
|
}
|
|
const lifecycle = new OwnedTabLifecycle(chrome);
|
const slotStore = new SlotStateStore(chrome);
|
const coordinator = new SlotCoordinator({
|
lifecycle,
|
slotStore,
|
connect: () => new NativePortTransport(chrome.runtime.connectNative(HOST)),
|
createSession,
|
hashSlot,
|
collectPage: collectVisiblePage
|
});
|
|
function schedule() {
|
chrome.alarms.create(ALARM, {when: nextBoundary(), periodInMinutes: PERIOD_MINUTES});
|
}
|
|
function recoverAtStartup() {
|
// Never leak a raw storage/runtime exception from a startup event. The next
|
// scheduled run will surface the same strict fixed code without acting.
|
void coordinator.recover().catch(() => console.error("E_SLOT_STATE"));
|
}
|
|
chrome.runtime.onInstalled.addListener(() => {
|
schedule();
|
recoverAtStartup();
|
});
|
|
chrome.runtime.onStartup.addListener(() => {
|
schedule();
|
recoverAtStartup();
|
});
|
|
chrome.alarms.onAlarm.addListener((alarm) => {
|
if (alarm?.name !== ALARM) return;
|
// The slot material is deterministic at the half-hour boundary and carries
|
// no session, cookie, profile, URL query, or page payload data.
|
const slot = Math.floor(Number(alarm.scheduledTime) / (PERIOD_MINUTES * 60 * 1000));
|
void coordinator.run(`dynamic-slot:${slot}`);
|
});
|