Files
goon_plugin/vite.config.ts
T
robot 542c5786be
CI / test (pull_request) Successful in 23s
feat: add HLS playback support with hls.js and modular viewer architecture
This PR adds end-to-end HLS stream detection, interception, and playback
to the viewer. The following root causes from the QA audit are fixed:

- Restore dropped HLS detection strategies (6-strategy per-tab query)
- Add deep-probe scanning for config globals and iframe manifests
- Add standalone interceptors.js for CSP-compliant runtime injection
- Fix viewer.js string entry handling for legacy storage entries
- Fix manifest.json web_accessible_resources for interceptors.js
- Disable modulePreload polyfill for Chrome MV3 compatibility
- Run deep probe and scraper in MAIN world for page global access
- Fallback to src as HLS manifest when hlsManifest field is missing

The viewer.js codebase has been refactored from a monolithic
wirePlaybackTracking() into modular single-responsibility functions:
PanelState, EventWiring, BufferMonitor, PanelRenderer, AggregateRenderer,
PanelFactory.

Additionally:
- Remove debug console.log statements from production code
- Fix dead code in m3u8DeepProbeFn walk() that prevented config global
  manifest detection
- Add eslint config with 141 rules and .eslintignore
- Add 44 new tests (83/83 total passing) covering HLS detection,
  config validation, edge cases, and integration scenarios
- Pin HLS.js CDN version to 1.5.17 (not floating/latest)
- Add grid-optimized HLS config with 10s buffer cap
- Add cleanup hooks for HLS instance destruction on page unload

Changed files (18):
- src/viewer/viewer.js (+834)
- src/viewer/index.js (+870)
- src/viewer/viewer.test.js (+976, new)
- src/viewer/index.test.js (+481, new)
- src/inject/interceptors.js (+169, new)
- src/content/content.js (+139)
- __tests__/hls_qa.test.mjs (+1168, new)
- manifest.json (+6)
- vite.config.ts (+5)
- __tests__/run.mjs (+2)
- __tests__/config_validation.test.mjs (+102, new)
- eslint.config.js (+24, new)
- .eslintignore (+1, new)
- QA_REPORT_HLS.md (+160, new)
- __tests__/data_flow.test.mjs (+/-5)
- __tests__/schema.test.mjs (+/-5)
- __tests__/viewer_integration.test.mjs (+/-4)
- src/background/background.js (-2)
2026-08-12 14:13:55 +00:00

74 lines
2.1 KiB
TypeScript

/**
* Vite config for the Chrome extension.
* Builds the extension into dist/ for loading as an unpacked extension.
*
* Usage:
* npm run dev — watch mode, rebuild on change
* npm run build — production build
*/
import { defineConfig } from 'vite';
import { resolve } from 'path';
import { copyFileSync, mkdirSync, writeFileSync } from 'fs';
import sharp from 'sharp';
export default defineConfig({
root: 'src',
plugins: [
{
name: 'generate-icons',
async generateBundle() {
const outDir = resolve(__dirname, 'dist');
mkdirSync(resolve(outDir, 'icons'), { recursive: true });
const sizes = [16, 48, 128];
const svgPath = resolve(__dirname, 'src/icons/icon.svg');
const outDirs = [
resolve(outDir, 'icons'),
resolve(__dirname, 'icons'),
];
for (const size of sizes) {
const buffer = await sharp(svgPath)
.resize(size, size)
.png()
.toBuffer();
for (const dir of outDirs) {
mkdirSync(dir, { recursive: true });
writeFileSync(resolve(dir, `icon${size}.png`), buffer);
}
}
},
},
{
name: 'copy-manifest',
generateBundle() {
const manifestPath = resolve(__dirname, 'manifest.json');
const outPath = resolve(__dirname, 'dist/manifest.json');
copyFileSync(manifestPath, outPath);
},
},
],
build: {
target: 'chrome110',
outDir: '../dist',
emptyOutDir: true,
modulePreload: {
polyfill: false,
},
rollupOptions: {
input: {
background: resolve(__dirname, 'src/background/background.js'),
content: resolve(__dirname, 'src/content/content.js'),
viewer: resolve(__dirname, 'src/viewer/viewer.html'),
viewerIndex: resolve(__dirname, 'src/viewer/index.html'),
interceptors: resolve(__dirname, 'src/inject/interceptors.js'),
},
output: {
entryFileNames: '[name].js',
chunkFileNames: 'chunks/[name].js',
assetFileNames: '[name][extname]',
},
},
},
});