Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased]

## [3.5.12] - 2026-05-11

### Fixed
- Background SW removed a dead chrome.storage.local self-healing path for the YouTube InnerTube client version. The hydration block read `sb_yt_client_version` on every SW wake but **no code anywhere ever wrote that key** — the comment claimed the maintenance alarm refreshed it, but the alarm only sends `version-check` against GitHub. The runtime override never triggered; `_BG_YT_CLIENT_VERSION` was effectively a const all along. Replaced the let + hydration block with a plain const + comment explaining the manual-bump workflow (in sync with `src/lib/constants.js` + `src/shared/constants.json` via `check-bg-sync.js`). Also removes the fire-and-forget storage race on every SW wake.
- `FETCH_URL` proxy handler now routes through `fetchWithRetry` instead of raw `fetch`. Previously a transient YouTube/InnerTube 5xx propagated straight to the content script while `GOOGLE_TRANSLATE_BATCH` got the retry contract — inconsistency the v3.5.8 fix was supposed to eliminate.
- `handleVersionCheck` (GitHub API call) likewise routes through `fetchWithRetry`. Anonymous GitHub quota is 60/h per IP; with users converging on residential ranges, 403s are common, and the previous code silently dropped them with a single attempt. The 4xx fail-fast contract still prevents pointless retries on 403/404.

### Changed
- Added `https://api.github.com/*` to `host_permissions` in `manifest.json`. SW `fetch` to undeclared origins works in MV3 but CWS reviewers flag undeclared hostnames; explicit declaration matches what the code actually does.

## [3.5.11] - 2026-05-07

### Security / Hardening
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<img src="assets/icons/icon128.png" alt="SkillBridge" width="90" />

# SkillBridge for Anthropic Academy <!-- VERSION_START -->v3.5.11<!-- VERSION_END -->
# SkillBridge for Anthropic Academy <!-- VERSION_START -->v3.5.12<!-- VERSION_END -->

> Available in multiple languages at the [project landing page](https://heznpc.github.io/skillBridge/).

Expand Down
4 changes: 2 additions & 2 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<title>SkillBridge — AI Course Translator for <!-- LANG_COUNT_START -->32+<!-- LANG_COUNT_END --> Languages</title>
<meta name="description" content="Free Chrome extension to translate Anthropic Academy courses into <!-- LANG_COUNT_START -->32+<!-- LANG_COUNT_END --> languages with AI-powered tutoring. No API keys needed.">
<meta property="og:title" content="SkillBridge — AI Course Translator">
<meta property="og:description" content="Learn Anthropic's free AI courses in your language. Powered by <!-- VERSION_START -->v3.5.11<!-- VERSION_END -->.">
<meta property="og:description" content="Learn Anthropic's free AI courses in your language. Powered by <!-- VERSION_START -->v3.5.12<!-- VERSION_END -->.">
<meta property="og:type" content="website">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
Expand Down Expand Up @@ -255,7 +255,7 @@
<body>
<section class="hero">
<div class="container">
<div class="hero-badge"><!-- VERSION_START -->v3.5.11<!-- VERSION_END --> &middot; Open Source &middot; Free &middot; No API Key</div>
<div class="hero-badge"><!-- VERSION_START -->v3.5.12<!-- VERSION_END --> &middot; Open Source &middot; Free &middot; No API Key</div>
<h1>Learn Anthropic Courses<br>In Your Language</h1>
<p>A Chrome extension that translates Anthropic Academy courses into <!-- LANG_COUNT_START -->32+<!-- LANG_COUNT_END --> languages with an AI-powered learning assistant.</p>
<p style="font-size:13px;opacity:0.85;margin-top:-16px;">Not affiliated with, endorsed by, or sponsored by Anthropic.</p>
Expand Down
5 changes: 3 additions & 2 deletions manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"manifest_version": 3,
"name": "__MSG_extName__",
"description": "__MSG_extDescription__",
"version": "3.5.11",
"version": "3.5.12",
"minimum_chrome_version": "120",
"author": "SkillBridge Contributors",
"homepage_url": "https://github.com/heznpc/skillbridge",
Expand All @@ -14,7 +14,8 @@
"host_permissions": [
"https://*.skilljar.com/*",
"https://*.youtube.com/*",
"https://translate.googleapis.com/*"
"https://translate.googleapis.com/*",
"https://api.github.com/*"
],
"action": {
"default_popup": "src/popup/popup.html",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "skillbridge",
"version": "3.5.11",
"version": "3.5.12",
"private": true,
"scripts": {
"test": "jest --verbose",
Expand Down
12 changes: 6 additions & 6 deletions scripts/check-bg-sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,15 @@ if (!bgMapMatch) {
}
}

// Extract _BG_YT_CLIENT_VERSION_DEFAULT fallback — this is the inline default
// that is always present; _BG_YT_CLIENT_VERSION itself is a `let` that gets
// hydrated from chrome.storage.local at runtime (see #78).
const bgVerMatch = bgSrc.match(/(?:const|let)\s+_BG_YT_CLIENT_VERSION_DEFAULT\s*=\s*'([^']+)'/);
// Extract _BG_YT_CLIENT_VERSION (since v3.5.12 this is a plain const — the
// previous storage-hydrated `let _BG_YT_CLIENT_VERSION_DEFAULT` was removed
// because no code ever wrote the storage key the override depended on).
const bgVerMatch = bgSrc.match(/const\s+_BG_YT_CLIENT_VERSION\s*=\s*'([^']+)'/);
if (!bgVerMatch) {
fail('Could not find _BG_YT_CLIENT_VERSION_DEFAULT in background.js');
fail('Could not find _BG_YT_CLIENT_VERSION in background.js');
} else if (bgVerMatch[1] !== jsonVersion) {
fail(
`_BG_YT_CLIENT_VERSION_DEFAULT fallback in background.js differs from constants.json\n background.js: ${bgVerMatch[1]}\n constants.json: ${jsonVersion}`,
`_BG_YT_CLIENT_VERSION in background.js differs from constants.json\n background.js: ${bgVerMatch[1]}\n constants.json: ${jsonVersion}`,
);
}

Expand Down
61 changes: 14 additions & 47 deletions src/background/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,42 +11,10 @@
// Shared constants — kept in sync with src/shared/constants.json via scripts/check-bg-sync.js
const _BG_GT_LANG_MAP = { 'zh-CN': 'zh-CN', 'zh-TW': 'zh-TW', 'pt-BR': 'pt' };

// YouTube's internal client version is bumped every few weeks. Storing the
// "default" inline as a fallback and overriding it from chrome.storage.local
// (refreshed by the maintenance alarm) means a stale build keeps working
// until the next 24h alarm tick rather than dying immediately.
const _BG_YT_CLIENT_VERSION_DEFAULT = '2.20260415.01.00';
let _BG_YT_CLIENT_VERSION = _BG_YT_CLIENT_VERSION_DEFAULT;

const _YT_VERSION_STORAGE_KEY = 'sb_yt_client_version';

// Hydrate the runtime override from storage on service worker start.
// Wrapped defensively because tests stub `chrome` with only the surfaces
// they exercise — production always has chrome.storage.local available.
if (
typeof chrome !== 'undefined' &&
chrome.storage &&
chrome.storage.local &&
typeof chrome.storage.local.get === 'function'
) {
try {
const result = chrome.storage.local.get(_YT_VERSION_STORAGE_KEY);
if (result && typeof result.then === 'function') {
result
.then((stored) => {
const v = stored && stored[_YT_VERSION_STORAGE_KEY];
if (typeof v === 'string' && /^\d+\.\d+\.\d+\.\d+$/.test(v)) {
_BG_YT_CLIENT_VERSION = v;
}
})
.catch(() => {
/* non-fatal */
});
}
} catch {
/* test stub may throw — non-fatal */
}
}
// YouTube's internal client version. Bumped manually when InnerTube rejects
// our value (observed every few weeks); kept in sync with the same constant
// in src/lib/constants.js + src/shared/constants.json via check-bg-sync.js.
const _BG_YT_CLIENT_VERSION = '2.20260415.01.00';

function gtLangCode(lang) {
return _BG_GT_LANG_MAP[lang] || lang;
Expand Down Expand Up @@ -181,13 +149,13 @@ async function handleVersionCheck() {
const manifest = chrome.runtime.getManifest();
const localVersion = manifest.version;

const resp = await fetch(`https://api.github.com/repos/${_GITHUB_REPO}/releases/latest`, {
// Anonymous GitHub API quota is 60/h per IP — with hundreds of users on
// the same residential ranges, 403s are common. fetchWithRetry's 4xx
// fail-fast bails immediately on 403/404 (no point retrying without auth)
// and backs off transient 5xx.
const resp = await fetchWithRetry(`https://api.github.com/repos/${_GITHUB_REPO}/releases/latest`, {
headers: { Accept: 'application/vnd.github.v3+json' },
});
if (!resp.ok) {
console.warn(`[SkillBridge] Version check: GitHub API returned ${resp.status}`);
return;
}
const release = await resp.json();
const remoteVersion = (release.tag_name || '').replace(/^v/, '');

Expand Down Expand Up @@ -272,14 +240,13 @@ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
}
}
fetchOpts.headers = headers;
fetch(msg.url, fetchOpts)
.then((resp) => {
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
return resp.text();
})
// Route through fetchWithRetry so 5xx/429s back off, 4xx fails fast,
// and the abuse-pattern contract is consistent with the GT path.
fetchWithRetry(msg.url, fetchOpts)
.then((resp) => resp.text())
.then((text) => sendResponse({ ok: true, data: text }))
.catch((err) => {
console.error(`[SkillBridge BG] Error: ${err.message}`);
console.error(`[SkillBridge BG] FETCH_URL error: ${err.message}`);
sendResponse({ ok: false, error: err.message });
});
return true;
Expand Down
2 changes: 1 addition & 1 deletion src/data/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"_meta": {
"lang": "de",
"langName": "Deutsch",
"version": "3.5.11",
"version": "3.5.12",
"lastUpdated": "2026-04-02"
},
"ui": {
Expand Down
2 changes: 1 addition & 1 deletion src/data/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"_meta": {
"lang": "es",
"langName": "Español",
"version": "3.5.11",
"version": "3.5.12",
"lastUpdated": "2026-04-02"
},
"ui": {
Expand Down
2 changes: 1 addition & 1 deletion src/data/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"_meta": {
"lang": "fr",
"langName": "Français",
"version": "3.5.11",
"version": "3.5.12",
"lastUpdated": "2026-04-02"
},
"ui": {
Expand Down
2 changes: 1 addition & 1 deletion src/data/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"_meta": {
"lang": "ja",
"langName": "日本語",
"version": "3.5.11",
"version": "3.5.12",
"lastUpdated": "2026-04-02"
},
"ui": {
Expand Down
2 changes: 1 addition & 1 deletion src/data/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"_meta": {
"lang": "ko",
"langName": "한국어",
"version": "3.5.11",
"version": "3.5.12",
"lastUpdated": "2026-04-02"
},
"ui": {
Expand Down
2 changes: 1 addition & 1 deletion src/data/pt-BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"_meta": {
"lang": "pt-BR",
"langName": "Português (BR)",
"version": "3.5.11",
"version": "3.5.12",
"lastUpdated": "2026-04-02"
},
"ui": {
Expand Down
2 changes: 1 addition & 1 deletion src/data/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"_meta": {
"lang": "ru",
"langName": "Русский",
"version": "3.5.11",
"version": "3.5.12",
"lastUpdated": "2026-04-02"
},
"ui": {
Expand Down
2 changes: 1 addition & 1 deletion src/data/vi.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"_meta": {
"lang": "vi",
"langName": "Tiếng Việt",
"version": "3.5.11",
"version": "3.5.12",
"lastUpdated": "2026-04-02"
},
"ui": {
Expand Down
2 changes: 1 addition & 1 deletion src/data/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"_meta": {
"lang": "zh-CN",
"langName": "简体中文",
"version": "3.5.11",
"version": "3.5.12",
"lastUpdated": "2026-04-02"
},
"ui": {
Expand Down
2 changes: 1 addition & 1 deletion src/data/zh-TW.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"_meta": {
"lang": "zh-TW",
"langName": "中文(繁體)",
"version": "3.5.11",
"version": "3.5.12",
"lastUpdated": "2026-04-02"
},
"ui": {
Expand Down
Loading