v0.5.3 — Fix resource swapper, add Husky, remove uuid
Fix three Electron 12→42 protocol migration bugs in the resource swapper: register protocol on the app session instead of default, generate valid URLs from Windows paths, and prevent non-swapped krunker.io requests from being cancelled. Swapper now rescans on page refresh to pick up file changes. Add Husky pre-commit hook to run ESLint. Remove unused uuid dependency. Update README with lint script, husky, and swapper improvements. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+11
-8
@@ -211,14 +211,14 @@ app.whenReady().then(async () => {
|
||||
async function launchApp(): Promise<void> {
|
||||
electronLog.log('[KCC] Starting initialization');
|
||||
|
||||
// ── Register swapper file protocol ──
|
||||
registerSwapperFileProtocol();
|
||||
|
||||
// ── Session: persistent partition + clean user-agent ──
|
||||
const ses = session.fromPartition('persist:krunker');
|
||||
const rawUA = ses.getUserAgent();
|
||||
ses.setUserAgent(rawUA.replace(/\s*krunker-civilian-client\/\S+/i, ''));
|
||||
|
||||
// ── Register swapper file protocol on this session ──
|
||||
registerSwapperFileProtocol(ses);
|
||||
|
||||
// ── Resource swapper ──
|
||||
const swapperConfig = config.get('swapper');
|
||||
const swapDir = swapperConfig.path || join(app.getPath('userData'), 'Krunker Civilian Client', 'swapper');
|
||||
@@ -240,16 +240,17 @@ async function launchApp(): Promise<void> {
|
||||
: [...BLOCKED_URL_PATTERNS];
|
||||
|
||||
ses.webRequest.onBeforeRequest({ urls: requestFilterUrls }, (details, callback) => {
|
||||
// Check swapper first — redirect matching assets to local files
|
||||
if (swapper) {
|
||||
const redirect = swapper.getRedirect(details.url);
|
||||
if (redirect) return callback({ redirectURL: redirect });
|
||||
}
|
||||
// If we got here via the broad krunker.io pattern (not an ad), let it through
|
||||
// Determine if this URL is a krunker.io request (matched by the broad swapper pattern)
|
||||
// vs an ad-block pattern. krunker.io requests that weren't swapped pass through normally.
|
||||
try {
|
||||
const host = new URL(details.url).hostname;
|
||||
if (host.endsWith('krunker.io')) return callback({});
|
||||
} catch { /* ignore invalid URLs */ }
|
||||
// Otherwise it matched an ad-block pattern — cancel it
|
||||
if (new URL(details.url).hostname.endsWith('krunker.io')) return callback({});
|
||||
} catch { /* invalid URL — fall through to cancel */ }
|
||||
// Matched an ad-block pattern — cancel it
|
||||
callback({ cancel: true });
|
||||
});
|
||||
|
||||
@@ -460,6 +461,8 @@ async function launchApp(): Promise<void> {
|
||||
// ── Inject scripts after page loads ──
|
||||
win.webContents.on('did-finish-load', () => {
|
||||
electronLog.log(`[KCC] Page loaded: ${win.webContents.getURL()}`);
|
||||
// Rescan swap directory so new/changed files are picked up on refresh
|
||||
if (swapper) swapper.rescan().catch(() => {});
|
||||
Promise.all([
|
||||
win.webContents.insertCSS(HIDE_ADS_CSS),
|
||||
win.webContents.insertCSS(ALL_CLIENT_CSS),
|
||||
|
||||
+41
-8
@@ -1,27 +1,53 @@
|
||||
import { existsSync, mkdirSync, promises as fsp } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { protocol, net } from 'electron';
|
||||
import { protocol, net, Session } from 'electron';
|
||||
|
||||
const PROTOCOL_NAME = 'kpc-swap';
|
||||
const TARGET_DOMAIN = 'krunker.io';
|
||||
|
||||
/**
|
||||
* Convert a native file path to a proper kpc-swap:// URL.
|
||||
* Windows paths like C:\foo\bar become kpc-swap://C/foo/bar
|
||||
*/
|
||||
function filePathToSwapURL(filePath: string): string {
|
||||
const forwardSlash = filePath.replace(/\\/g, '/');
|
||||
// Windows drive letter: C:/foo → kpc-swap://C/foo
|
||||
const match = forwardSlash.match(/^([A-Za-z]):\/(.*)/);
|
||||
if (match) {
|
||||
return `${PROTOCOL_NAME}://${match[1]}/${match[2]}`;
|
||||
}
|
||||
// Unix absolute: /home/user/foo → kpc-swap:///home/user/foo
|
||||
return `${PROTOCOL_NAME}://${forwardSlash}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the custom protocol scheme. Must be called BEFORE app.ready.
|
||||
*/
|
||||
export function initSwapperProtocol(): void {
|
||||
protocol.registerSchemesAsPrivileged([{
|
||||
scheme: PROTOCOL_NAME,
|
||||
privileges: { secure: true, corsEnabled: true, bypassCSP: true },
|
||||
privileges: { standard: true, secure: true, corsEnabled: true, bypassCSP: true },
|
||||
}]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the file protocol handler. Must be called AFTER app.ready.
|
||||
* Register the file protocol handler on the given session.
|
||||
* Must be called AFTER app.ready.
|
||||
*/
|
||||
export function registerSwapperFileProtocol(): void {
|
||||
protocol.handle(PROTOCOL_NAME, (request) => {
|
||||
const filePath = decodeURI(request.url.replace(`${PROTOCOL_NAME}:`, ''));
|
||||
return net.fetch('file://' + filePath);
|
||||
export function registerSwapperFileProtocol(ses: Session): void {
|
||||
ses.protocol.handle(PROTOCOL_NAME, (request) => {
|
||||
const url = new URL(request.url);
|
||||
// Reconstruct the file path from the URL
|
||||
// Windows: kpc-swap://C/foo/bar → C:/foo/bar
|
||||
// Unix: kpc-swap:///home/foo → /home/foo
|
||||
let filePath: string;
|
||||
if (url.hostname) {
|
||||
// Windows drive letter is the hostname
|
||||
filePath = `${url.hostname}:${url.pathname}`;
|
||||
} else {
|
||||
filePath = url.pathname;
|
||||
}
|
||||
return net.fetch(`file://${filePath}`);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -47,6 +73,13 @@ export class ResourceSwapper {
|
||||
this.ready = true;
|
||||
}
|
||||
|
||||
/** Rescan the swap directory to pick up added/removed/changed files */
|
||||
async rescan(): Promise<void> {
|
||||
this.swapFiles.clear();
|
||||
await this.scanAsync('');
|
||||
this.ready = true;
|
||||
}
|
||||
|
||||
/** URL filter patterns for webRequest.onBeforeRequest — single broad pattern */
|
||||
get patterns(): string[] {
|
||||
return this.swapFiles.size > 0 ? [`*://*.${TARGET_DOMAIN}/*`] : [];
|
||||
@@ -70,7 +103,7 @@ export class ResourceSwapper {
|
||||
let pathname = queryStart === -1 ? url.substring(pathStart) : url.substring(pathStart, queryStart);
|
||||
if (pathname.startsWith('/assets/')) pathname = pathname.substring(7);
|
||||
const localPath = this.swapFiles.get(pathname);
|
||||
if (localPath) return `${PROTOCOL_NAME}:/${localPath}`;
|
||||
if (localPath) return filePathToSwapURL(localPath);
|
||||
} catch { /* malformed URL — ignore */ }
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user