mirror of
https://github.com/Shik3i/KoalaSync.git
synced 2026-07-27 20:39:10 +00:00
docs: refresh onboarding and examples
This commit is contained in:
+78
-18
@@ -1,33 +1,93 @@
|
||||
# Development Scripts
|
||||
|
||||
This directory contains utility scripts for the KoalaSync development workflow.
|
||||
This directory contains build, synchronization, and verification scripts for the KoalaSync workspace. Run all commands from the repository root unless a script says otherwise.
|
||||
|
||||
## Main Commands
|
||||
|
||||
```bash
|
||||
npm run build:extension
|
||||
npm run verify
|
||||
npm run lint
|
||||
npm run test:unit
|
||||
```
|
||||
|
||||
- `npm run build:extension` runs `scripts/build-extension.cjs`.
|
||||
- `npm run verify` runs the full release-safety suite in `scripts/verify-release.mjs`.
|
||||
- `npm run lint` runs ESLint across the repository.
|
||||
- `npm run test:unit` runs Vitest tests.
|
||||
|
||||
## build-extension.cjs
|
||||
|
||||
The primary build tool for KoalaSync. This Node.js script automates two critical tasks:
|
||||
The primary extension build tool performs these steps:
|
||||
|
||||
1. **Protocol Synchronization**: Copies the "Single Source of Truth" constants (`shared/constants.js`) and the domain blacklist (`shared/blacklist.js`) from the root `/shared` directory into the `extension/shared/` directory.
|
||||
2. **Content Script Injection**: Injects protocol constants directly into `content.js` using marker-based replacement. This is necessary because `content.js` executes synchronously and cannot use ES module imports.
|
||||
3. **Artifact Generation**: Compiles the extension into browser-specific bundles for Chrome and Firefox, located in the `dist/` directory.
|
||||
|
||||
### Usage
|
||||
|
||||
From the **repository root**, run:
|
||||
1. Recreates `dist/`.
|
||||
2. Copies `shared/constants.js`, `shared/blacklist.js`, `shared/names.js`, and `shared/README.md` into `extension/shared/`.
|
||||
3. Injects synchronous shared values into `content.js`.
|
||||
4. Injects browser-specific uninstall URL constants into `background.js`.
|
||||
5. Injects the build timestamp into `popup.html`.
|
||||
6. Generates browser-specific manifests for Chrome and Firefox.
|
||||
7. Creates `dist/koalasync-chrome.zip` and `dist/koalasync-firefox.zip`.
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
node scripts/build-extension.cjs
|
||||
# or
|
||||
npm run build:extension
|
||||
```
|
||||
|
||||
### Why this script exists
|
||||
KoalaSync uses **Vanilla JS** in the extension to maintain zero runtime dependencies and maximum privacy. Since we don't use a bundler (like Webpack or Vite) inside the extension, this script serves as our lightweight "pre-build" step to ensure that the protocol constants remain synchronized between the extension and the relay server.
|
||||
## Injection Markers
|
||||
|
||||
### Content Injection Markers
|
||||
The build script uses marker comments/placeholders. Missing markers are a hard build failure so release artifacts cannot silently contain stale protocol data.
|
||||
|
||||
The build script uses marker comments in `content.js` to locate and replace constant blocks:
|
||||
| Target | Marker / Placeholder | Injected Value | Source |
|
||||
|:---|:---|:---|:---|
|
||||
| `content.js` | `SHARED_EVENTS_INJECT_START` / `END` | Full `EVENTS` object | `shared/constants.js` |
|
||||
| `content.js` | `SHARED_HEARTBEAT_INJECT_START` / `END` | `HEARTBEAT_INTERVAL` | `shared/constants.js` |
|
||||
| `content.js` | `SHARED_EPISODE_UTILS_INJECT_START` / `END` | `extractEpisodeId()` and `sameEpisode()` | `extension/episode-utils.js` |
|
||||
| `background.js` | `UNINSTALL_URL_INJECT_START` / `END` | Uninstall URL and browser type | `scripts/build-extension.cjs` |
|
||||
| `popup.html` | `__BUILD_TIMESTAMP__` | UTC build timestamp | Build time |
|
||||
|
||||
| Marker Pair | Injected Value | Source |
|
||||
|:---|:---|:---|
|
||||
| `SHARED_EVENTS_INJECT_START` / `END` | The full `EVENTS` object | `shared/constants.js` |
|
||||
| `SHARED_HEARTBEAT_INJECT_START` / `END` | `HEARTBEAT_INTERVAL` value | `shared/constants.js` |
|
||||
Do not remove or rename these markers without updating the build script and tests.
|
||||
|
||||
> **⚠️ Do NOT remove or modify these marker comments in `content.js`.** They are required for the build script to function. If the markers are missing, the build will fail with a clear error message.
|
||||
## Verification Suite
|
||||
|
||||
`scripts/verify-release.mjs` is the best single command before release, PR review, or handoff:
|
||||
|
||||
```bash
|
||||
npm run verify
|
||||
```
|
||||
|
||||
It currently runs:
|
||||
|
||||
- Vitest unit tests.
|
||||
- Server ops, route, WebSocket, and rate-limiter checks.
|
||||
- Episode parser, title privacy, audio settings, popup cooldown, names, and content-video-finder checks.
|
||||
- JavaScript syntax checks for server and extension entry points.
|
||||
- Extension and website locale coverage checks.
|
||||
- ESLint.
|
||||
- Production `npm audit` checks for root and server dependencies.
|
||||
- Extension build and website build.
|
||||
|
||||
## Focused Scripts
|
||||
|
||||
| Script | Purpose |
|
||||
|:---|:---|
|
||||
| `test-server-ops.mjs` | Health payload and admin metrics helpers |
|
||||
| `test-server-routes.mjs` | HTTP health routes, caching, and admin metrics access |
|
||||
| `test-server-ws.mjs` | Socket.IO relay integration, including host-control behavior |
|
||||
| `test-rate-limiter.mjs` | Rate-limiter map and cooldown behavior |
|
||||
| `test-episode-utils.mjs` | Episode-title extraction and comparison |
|
||||
| `test-title-privacy.mjs` | Tab/media title privacy sanitization |
|
||||
| `test-audio-settings.mjs` | Audio settings defaults and normalization |
|
||||
| `test-popup-refresh-cooldown.mjs` | Popup refresh throttling behavior |
|
||||
| `test-names.mjs` | Generated username format and coverage |
|
||||
| `test-content-video-finder.cjs` | Content-script video selection helpers |
|
||||
| `test-locales.cjs` | Extension runtime and browser-store locale coverage |
|
||||
| `test-website-locales.mjs` | Website locale coverage |
|
||||
|
||||
## Do Not Break
|
||||
|
||||
- Keep scripts runnable from the repository root.
|
||||
- Keep build output under `dist/` and generated website output under `website/www/`.
|
||||
- Keep shared protocol sync automated; do not add manual copy steps.
|
||||
- Treat warnings in verification scripts as release blockers unless the script explicitly documents them as informational.
|
||||
|
||||
+22
-27
@@ -36,6 +36,13 @@ console.log('✓ constants.js, blacklist.js, names.js, and README.md synced to e
|
||||
// Read the base manifest
|
||||
const baseManifest = JSON.parse(fs.readFileSync(baseManifestPath, 'utf8'));
|
||||
|
||||
function replaceRequiredBlock(content, pattern, replacement, description) {
|
||||
if (!pattern.test(content)) {
|
||||
throw new Error(`CRITICAL: ${description} markers not found. Aborting build to prevent stale artifacts.`);
|
||||
}
|
||||
return content.replace(pattern, replacement);
|
||||
}
|
||||
|
||||
// Helper to copy files, ignoring manifest.json and manifest.base.json
|
||||
// Also injects shared constants into content.js
|
||||
function copyExtensionFiles(targetDir, browserName) {
|
||||
@@ -79,11 +86,7 @@ function copyExtensionFiles(targetDir, browserName) {
|
||||
const ePattern = new RegExp(`${eStart}[\\s\\S]+?${eEnd}`);
|
||||
const eRep = `${eStart}\n // This block is automatically updated by /scripts/build-extension.cjs\n const EVENTS = ${eventsObject};\n ${eEnd}`;
|
||||
|
||||
if (ePattern.test(content)) {
|
||||
content = content.replace(ePattern, eRep);
|
||||
} else {
|
||||
console.warn('⚠️ WARNING: Event markers not found in content.js');
|
||||
}
|
||||
content = replaceRequiredBlock(content, ePattern, eRep, 'Event injection');
|
||||
|
||||
// 2. Inject Heartbeat
|
||||
const hStart = '// --- SHARED_HEARTBEAT_INJECT_START ---';
|
||||
@@ -91,30 +94,23 @@ function copyExtensionFiles(targetDir, browserName) {
|
||||
const hPattern = new RegExp(`${hStart}[\\s\\S]+?${hEnd}`);
|
||||
const hRep = `${hStart}\n const HEARTBEAT_INTERVAL_VAL = ${heartbeatVal};\n ${hEnd}`;
|
||||
|
||||
if (hPattern.test(content)) {
|
||||
content = content.replace(hPattern, hRep);
|
||||
} else {
|
||||
console.warn('⚠️ WARNING: Heartbeat markers not found in content.js');
|
||||
}
|
||||
content = replaceRequiredBlock(content, hPattern, hRep, 'Heartbeat injection');
|
||||
|
||||
// 3. Inject Episode Utils
|
||||
const euStart = '// --- SHARED_EPISODE_UTILS_INJECT_START ---';
|
||||
const euEnd = '// --- SHARED_EPISODE_UTILS_INJECT_END ---';
|
||||
const euPattern = new RegExp(euStart.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '[\\s\\S]+?' + euEnd.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
||||
const euPath = path.join(rootDir, 'extension', 'episode-utils.js');
|
||||
if (fs.existsSync(euPath)) {
|
||||
const euContent = fs.readFileSync(euPath, 'utf8');
|
||||
const stripped = euContent
|
||||
.replace(/^\/\*\*[\s\S]*?\*\/\s*/m, '')
|
||||
.replace(/export function /g, 'function ')
|
||||
.trim();
|
||||
const euRep = `${euStart}\n // This block is automatically updated by /scripts/build-extension.cjs\n${stripped.split('\n').map(l => ' ' + l).join('\n')}\n ${euEnd}`;
|
||||
if (euPattern.test(content)) {
|
||||
content = content.replace(euPattern, euRep);
|
||||
} else {
|
||||
console.warn('⚠ WARNING: Episode utils markers not found in content.js');
|
||||
}
|
||||
if (!fs.existsSync(euPath)) {
|
||||
throw new Error(`CRITICAL: Episode utils source missing: ${euPath}. Aborting build.`);
|
||||
}
|
||||
const euContent = fs.readFileSync(euPath, 'utf8');
|
||||
const stripped = euContent
|
||||
.replace(/^\/\*\*[\s\S]*?\*\/\s*/m, '')
|
||||
.replace(/export function /g, 'function ')
|
||||
.trim();
|
||||
const euRep = `${euStart}\n // This block is automatically updated by /scripts/build-extension.cjs\n${stripped.split('\n').map(l => ' ' + l).join('\n')}\n ${euEnd}`;
|
||||
content = replaceRequiredBlock(content, euPattern, euRep, 'Episode utils injection');
|
||||
|
||||
fs.writeFileSync(destPath, content);
|
||||
console.log('✓ Injected shared constants into content.js');
|
||||
@@ -132,17 +128,16 @@ function copyExtensionFiles(targetDir, browserName) {
|
||||
uRep += ` const BROWSER_TYPE = "${browserName}";\n`;
|
||||
uRep += ` ${uEnd}`;
|
||||
|
||||
if (uPattern.test(content)) {
|
||||
content = content.replace(uPattern, uRep);
|
||||
} else {
|
||||
console.warn('⚠️ WARNING: Uninstall URL markers not found in background.js');
|
||||
}
|
||||
content = replaceRequiredBlock(content, uPattern, uRep, 'Uninstall URL injection');
|
||||
|
||||
fs.writeFileSync(destPath, content);
|
||||
console.log(`✓ Injected uninstall URL constants for ${browserName} into background.js`);
|
||||
} else if (item === 'popup.html') {
|
||||
let content = fs.readFileSync(srcPath, 'utf8');
|
||||
const timestamp = new Date().toISOString().replace('T', ' ').substring(0, 19) + ' UTC';
|
||||
if (!content.includes('__BUILD_TIMESTAMP__')) {
|
||||
throw new Error('CRITICAL: Build timestamp placeholder not found in popup.html. Aborting build.');
|
||||
}
|
||||
content = content.replace(/__BUILD_TIMESTAMP__/g, timestamp);
|
||||
fs.writeFileSync(destPath, content);
|
||||
console.log(`✓ Injected build timestamp into popup.html: ${timestamp}`);
|
||||
|
||||
Reference in New Issue
Block a user