export const COI_STATE = {
SW_INSTALLING: 'SW_INSTALLING',
NEEDS_RELOAD: 'NEEDS_RELOAD',
READY: 'READY',
DEGRADED: 'DEGRADED',
};
let activeReloadController = null;
const serviceWorkerActivationCallbacks = new Set();
let serviceWorkerActivationListenersInstalled = false;
let serviceWorkerActivationDispatchScheduled = false;
function hashScopeId(input) {
let hash = 0x811c9dc5;
for (let i = 0; i < input.length; i++) {
hash ^= input.charCodeAt(i);
hash = Math.imul(hash, 0x01000193) >>> 0;
}
return hash.toString(16).padStart(8, '0');
}
function getSetupCompleteKey() {
try {
return `cass-coi-setup-complete-${hashScopeId(new URL('./', window.location.href).href)}`;
} catch {
const href = typeof window?.location?.href === 'string'
? window.location.href
: 'unknown';
return `cass-coi-setup-complete-${hashScopeId(href.split('#')[0].split('?')[0])}`;
}
}
export function isSetupComplete() {
try {
return localStorage.getItem(getSetupCompleteKey()) === 'true';
} catch {
return false;
}
}
export function markSetupComplete() {
try {
localStorage.setItem(getSetupCompleteKey(), 'true');
} catch {
}
}
export function clearSetupComplete() {
try {
localStorage.removeItem(getSetupCompleteKey());
} catch {
}
}
async function getCurrentServiceWorkerRegistration() {
if (!('serviceWorker' in navigator)) {
return null;
}
try {
return (await navigator.serviceWorker.getRegistration()) ?? null;
} catch {
return null;
}
}
export function isCrossOriginIsolated() {
return window.crossOriginIsolated === true;
}
export async function isServiceWorkerActive() {
const registration = await getCurrentServiceWorkerRegistration();
return registration?.active !== null && registration?.active !== undefined;
}
export async function hasServiceWorkerRegistration() {
return (await getCurrentServiceWorkerRegistration()) !== null;
}
export function isServiceWorkerSupported() {
return 'serviceWorker' in navigator;
}
export function isSharedArrayBufferAvailable() {
try {
new SharedArrayBuffer(1);
return true;
} catch {
return false;
}
}
export async function getCOIState() {
if (!isServiceWorkerSupported()) {
console.log('[COI] Service Workers not supported - degraded mode');
return COI_STATE.DEGRADED;
}
const swActive = await isServiceWorkerActive();
const coiEnabled = isCrossOriginIsolated();
const sabAvailable = isSharedArrayBufferAvailable();
console.log('[COI] State check:', { swActive, coiEnabled, sabAvailable });
if (!swActive) {
return COI_STATE.SW_INSTALLING;
}
if (!coiEnabled || !sabAvailable) {
return COI_STATE.NEEDS_RELOAD;
}
return COI_STATE.READY;
}
export function getArgon2Config() {
if (isSharedArrayBufferAvailable()) {
return {
parallelism: 4, mode: 'wasm-mt', expectedTime: '1-3s',
};
} else {
return {
parallelism: 1, mode: 'wasm-st', expectedTime: '3-9s',
};
}
}
export function showInstallingUI(container) {
container.innerHTML = `
<div class="coi-status installing">
<div class="coi-header">
<span class="coi-logo" aria-hidden="true">🔐</span>
<h3>Setting Up Secure Environment</h3>
</div>
<p class="coi-detail">One-time setup for fast, secure decryption</p>
<div class="coi-progress-steps">
<div class="coi-step" id="coi-step-sw" data-status="loading">
<span class="coi-step-icon" aria-hidden="true">⏳</span>
<span class="coi-step-text">Installing security worker...</span>
</div>
<div class="coi-step" id="coi-step-headers" data-status="pending">
<span class="coi-step-icon" aria-hidden="true">○</span>
<span class="coi-step-text">Activating isolation headers...</span>
</div>
</div>
</div>
`;
container.classList.remove('hidden');
}
export function updateProgressStep(stepId, status) {
const step = document.getElementById(stepId);
if (!step) return;
step.dataset.status = status;
const icon = step.querySelector('.coi-step-icon');
if (icon) {
switch (status) {
case 'loading':
icon.innerHTML = '⏳'; break;
case 'complete':
icon.innerHTML = '✅'; break;
case 'error':
icon.innerHTML = '❌'; break;
default:
icon.innerHTML = '○'; }
}
}
export function showReloadRequiredUI(container, options = {}) {
const { onReload = null, countdownSeconds = 3, autoReload = true } = options;
if (activeReloadController) {
activeReloadController.cancel();
activeReloadController = null;
}
container.innerHTML = `
<div class="coi-status needs-reload">
<div class="coi-header">
<span class="coi-logo" aria-hidden="true">🔐</span>
<h3>Almost There!</h3>
</div>
<div class="coi-progress-steps">
<div class="coi-step" data-status="complete">
<span class="coi-step-icon" aria-hidden="true">✅</span>
<span class="coi-step-text">Security worker installed</span>
</div>
<div class="coi-step" data-status="loading">
<span class="coi-step-icon" aria-hidden="true">⏳</span>
<span class="coi-step-text">Activating isolation headers...</span>
</div>
</div>
<div class="coi-reload-section">
<p class="coi-reload-message">One-time page reload required to enable optimal performance.</p>
<div id="coi-countdown-wrapper" class="coi-countdown-wrapper ${autoReload ? '' : 'hidden'}">
<span class="coi-countdown-text">Reloading in </span>
<span id="coi-countdown-number" class="coi-countdown-number">${countdownSeconds}</span>
<span class="coi-countdown-text">...</span>
</div>
<div class="coi-reload-buttons">
<button id="coi-reload-btn" class="btn btn-primary coi-reload-btn">
Reload Now
</button>
<button id="coi-cancel-btn" class="btn btn-secondary coi-cancel-btn ${autoReload ? '' : 'hidden'}">
Cancel
</button>
</div>
</div>
<details class="coi-details">
<summary>Why is this needed?</summary>
<p>
Modern browsers require special security headers for
hardware-accelerated encryption. After reloading, the
archive will:
</p>
<ul>
<li>Decrypt 3-5x faster using parallel processing</li>
<li>Support offline access</li>
<li>Use enhanced memory protection</li>
</ul>
<p class="coi-note">This is a one-time setup per browser.</p>
</details>
</div>
`;
container.classList.remove('hidden');
const reloadBtn = document.getElementById('coi-reload-btn');
const cancelBtn = document.getElementById('coi-cancel-btn');
const countdownWrapper = document.getElementById('coi-countdown-wrapper');
const countdownNumber = document.getElementById('coi-countdown-number');
let countdown = countdownSeconds;
let timerId = null;
const doReload = () => {
if (timerId) {
clearInterval(timerId);
timerId = null;
}
if (activeReloadController === control) {
activeReloadController = null;
}
if (onReload) {
onReload();
}
window.location.reload();
};
const cancelCountdown = () => {
if (timerId) {
clearInterval(timerId);
timerId = null;
}
if (countdownWrapper) {
countdownWrapper.classList.add('hidden');
}
if (cancelBtn) {
cancelBtn.classList.add('hidden');
}
if (activeReloadController === control) {
activeReloadController = null;
}
};
if (reloadBtn) {
reloadBtn.addEventListener('click', doReload);
}
if (cancelBtn) {
cancelBtn.addEventListener('click', cancelCountdown);
}
if (autoReload && countdownNumber) {
timerId = setInterval(() => {
countdown--;
if (countdown <= 0) {
doReload();
} else {
countdownNumber.textContent = countdown.toString();
}
}, 1000);
}
const control = {
cancel: cancelCountdown,
reload: doReload,
};
activeReloadController = control;
return control;
}
export function showDegradedModeWarning() {
if (document.querySelector('.coi-degraded-banner')) return;
const banner = document.createElement('div');
banner.className = 'coi-degraded-banner';
banner.innerHTML = `
<span class="coi-warning-icon">⚠️</span>
<span class="coi-warning-text">Running in compatibility mode - unlock may take longer</span>
<button class="coi-dismiss-btn" aria-label="Dismiss">✕</button>
`;
const dismissBtn = banner.querySelector('.coi-dismiss-btn');
if (dismissBtn) {
dismissBtn.addEventListener('click', () => {
banner.remove();
});
}
document.body.prepend(banner);
}
export function hideStatusUI(container) {
if (activeReloadController) {
activeReloadController.cancel();
activeReloadController = null;
}
container.classList.add('hidden');
container.innerHTML = '';
}
export async function initCOIDetection({
statusContainer,
authContainer,
onReady,
maxWaitMs = 5000,
autoReload = true,
countdownSeconds = 3,
}) {
let state = await getCOIState();
console.log('[COI] Initial state:', state);
if (state === COI_STATE.READY && isSetupComplete()) {
console.log('[COI] Setup already complete - fast path');
hideStatusUI(statusContainer);
if (onReady) onReady();
return state;
}
if (state === COI_STATE.SW_INSTALLING) {
showInstallingUI(statusContainer);
if ('serviceWorker' in navigator) {
try {
await Promise.race([
navigator.serviceWorker.ready,
new Promise((_, reject) =>
setTimeout(() => reject(new Error('SW timeout')), maxWaitMs)
),
]);
updateProgressStep('coi-step-sw', 'complete');
updateProgressStep('coi-step-headers', 'loading');
state = await getCOIState();
console.log('[COI] State after SW ready:', state);
} catch (error) {
console.warn('[COI] SW wait timeout or error:', error.message);
state = await getCOIState();
}
}
}
switch (state) {
case COI_STATE.READY:
console.log('[COI] Ready - proceeding to auth');
markSetupComplete();
hideStatusUI(statusContainer);
if (onReady) onReady();
break;
case COI_STATE.NEEDS_RELOAD:
console.log('[COI] Needs reload - showing prompt');
showReloadRequiredUI(statusContainer, {
autoReload,
countdownSeconds,
onReload: () => console.log('[COI] Reloading...'),
});
if (authContainer) {
authContainer.classList.add('hidden');
}
break;
case COI_STATE.DEGRADED:
console.log('[COI] Degraded mode - showing warning and proceeding');
markSetupComplete(); hideStatusUI(statusContainer);
showDegradedModeWarning();
if (onReady) onReady();
break;
case COI_STATE.SW_INSTALLING:
console.log('[COI] SW still installing - checking fallback');
if (!await hasServiceWorkerRegistration()) {
console.warn('[COI] No service worker registration found after waiting - degrading');
hideStatusUI(statusContainer);
showDegradedModeWarning();
if (onReady) onReady();
return COI_STATE.DEGRADED;
}
if (isSharedArrayBufferAvailable()) {
markSetupComplete();
hideStatusUI(statusContainer);
if (onReady) onReady();
} else {
showReloadRequiredUI(statusContainer, {
autoReload,
countdownSeconds,
onReload: () => console.log('[COI] Reloading...'),
});
if (authContainer) {
authContainer.classList.add('hidden');
}
}
break;
}
return state;
}
export function onServiceWorkerActivated(callback) {
if (!('serviceWorker' in navigator) || typeof callback !== 'function') {
return () => {};
}
serviceWorkerActivationCallbacks.add(callback);
if (!serviceWorkerActivationListenersInstalled) {
const notifyActivation = (reason) => {
if (serviceWorkerActivationDispatchScheduled) {
return;
}
serviceWorkerActivationDispatchScheduled = true;
queueMicrotask(() => {
serviceWorkerActivationDispatchScheduled = false;
console.log('[COI] Service worker activation detected:', reason);
[...serviceWorkerActivationCallbacks].forEach((registeredCallback) => {
try {
Promise.resolve(registeredCallback()).catch((error) => {
console.error('[COI] Activation callback failed:', error);
});
} catch (error) {
console.error('[COI] Activation callback failed:', error);
}
});
});
};
navigator.serviceWorker.addEventListener('message', (event) => {
if (event.data?.type === 'SW_ACTIVATED') {
notifyActivation('message');
}
});
navigator.serviceWorker.addEventListener('controllerchange', () => {
notifyActivation('controllerchange');
});
serviceWorkerActivationListenersInstalled = true;
}
return () => {
serviceWorkerActivationCallbacks.delete(callback);
};
}
export default {
COI_STATE,
isCrossOriginIsolated,
isServiceWorkerActive,
hasServiceWorkerRegistration,
isServiceWorkerSupported,
isSharedArrayBufferAvailable,
getCOIState,
getArgon2Config,
showInstallingUI,
showReloadRequiredUI,
showDegradedModeWarning,
hideStatusUI,
initCOIDetection,
onServiceWorkerActivated,
updateProgressStep,
isSetupComplete,
markSetupComplete,
clearSetupComplete,
};