browser-extension-manager 1.3.49 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -70
- package/dist/background.js +6 -1
- package/dist/build.js +5 -7
- package/dist/cli.js +1 -0
- package/dist/commands/install.js +1 -1
- package/dist/commands/test.js +55 -0
- package/dist/content.js +4 -0
- package/dist/defaults/CHANGELOG.md +15 -0
- package/dist/defaults/CLAUDE.md +82 -6
- package/dist/defaults/docs/README.md +17 -0
- package/dist/defaults/test/README.md +32 -0
- package/dist/defaults/test/_init.js +10 -0
- package/dist/gulp/tasks/defaults.js +15 -2
- package/dist/offscreen.js +4 -0
- package/dist/options.js +4 -0
- package/dist/page.js +4 -0
- package/dist/popup.js +4 -0
- package/dist/sidepanel.js +4 -0
- package/dist/test/assert.js +120 -0
- package/dist/test/fixtures/consumer-extension/dist/background.js +15 -0
- package/dist/test/fixtures/consumer-extension/dist/manifest.json +20 -0
- package/dist/test/fixtures/consumer-extension/dist/options.html +10 -0
- package/dist/test/fixtures/consumer-extension/dist/popup.html +11 -0
- package/dist/test/harness/extension/background.js +26 -0
- package/dist/test/harness/extension/manifest.json +27 -0
- package/dist/test/harness/extension/options.html +12 -0
- package/dist/test/harness/extension/popup.html +12 -0
- package/dist/test/harness/extension/sidepanel.html +12 -0
- package/dist/test/index.js +63 -0
- package/dist/test/runner.js +469 -0
- package/dist/test/runners/boot.js +201 -0
- package/dist/test/runners/chromium.js +399 -0
- package/dist/test/suites/background/messaging.test.js +42 -0
- package/dist/test/suites/background/storage.test.js +44 -0
- package/dist/test/suites/background/sw-context.test.js +47 -0
- package/dist/test/suites/boot/extension-loads.test.js +56 -0
- package/dist/test/suites/build/affiliatizer.test.js +63 -0
- package/dist/test/suites/build/cli.test.js +123 -0
- package/dist/test/suites/build/expect.test.js +47 -0
- package/dist/test/suites/build/exports.test.js +52 -0
- package/dist/test/suites/build/extension-fallback.test.js +41 -0
- package/dist/test/suites/build/logger-lite.test.js +59 -0
- package/dist/test/suites/build/manager.test.js +184 -0
- package/dist/test/suites/build/mode-helpers.test.js +135 -0
- package/dist/test/suites/view/options-and-sidepanel.test.js +14 -0
- package/dist/test/suites/view/popup-context.test.js +51 -0
- package/dist/test/suites/view/sidepanel.test.js +14 -0
- package/dist/utils/mode-helpers.js +117 -0
- package/package.json +7 -4
package/README.md
CHANGED
|
@@ -5,129 +5,171 @@
|
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<img src="https://img.shields.io/github/package-json/v/itw-creative-works/
|
|
8
|
+
<img src="https://img.shields.io/github/package-json/v/itw-creative-works/browser-extension-manager.svg">
|
|
9
9
|
<br>
|
|
10
|
-
<img src="https://img.shields.io/librariesio/release/npm/
|
|
11
|
-
<img src="https://img.shields.io/bundlephobia/min/
|
|
12
|
-
<img src="https://img.shields.io/
|
|
13
|
-
<img src="https://img.shields.io/
|
|
14
|
-
<img src="https://img.shields.io/
|
|
15
|
-
<img src="https://img.shields.io/
|
|
16
|
-
<img src="https://img.shields.io/github/
|
|
17
|
-
<img src="https://img.shields.io/github/contributors/itw-creative-works/ultimate-browser-extension.svg">
|
|
18
|
-
<img src="https://img.shields.io/github/last-commit/itw-creative-works/ultimate-browser-extension.svg">
|
|
10
|
+
<img src="https://img.shields.io/librariesio/release/npm/browser-extension-manager.svg">
|
|
11
|
+
<img src="https://img.shields.io/bundlephobia/min/browser-extension-manager.svg">
|
|
12
|
+
<img src="https://img.shields.io/npm/dm/browser-extension-manager.svg">
|
|
13
|
+
<img src="https://img.shields.io/node/v/browser-extension-manager.svg">
|
|
14
|
+
<img src="https://img.shields.io/github/license/itw-creative-works/browser-extension-manager.svg">
|
|
15
|
+
<img src="https://img.shields.io/github/contributors/itw-creative-works/browser-extension-manager.svg">
|
|
16
|
+
<img src="https://img.shields.io/github/last-commit/itw-creative-works/browser-extension-manager.svg">
|
|
19
17
|
<br>
|
|
20
18
|
<br>
|
|
21
|
-
<a href="https://itwcreativeworks.com">Site</a> | <a href="https://www.npmjs.com/package/
|
|
19
|
+
<a href="https://itwcreativeworks.com">Site</a> | <a href="https://www.npmjs.com/package/browser-extension-manager">NPM Module</a> | <a href="https://github.com/itw-creative-works/browser-extension-manager">GitHub Repo</a>
|
|
22
20
|
<br>
|
|
23
21
|
<br>
|
|
24
|
-
<strong>
|
|
22
|
+
<strong>Browser Extension Manager</strong> is a framework for building modern cross-browser extensions. One-line bootstrap per context, component-based architecture, multi-browser build pipeline, cross-context auth, auto-translation across 16 languages, and a four-layer test framework.
|
|
25
23
|
</p>
|
|
26
24
|
|
|
27
25
|
## 🦄 Features
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
|
|
27
|
+
- **Build for any browser**: Chrome, Firefox, Edge, Opera, Brave
|
|
28
|
+
- **Component architecture**: seven contexts (background / popup / options / sidepanel / content / pages / offscreen) each with view + styles + script
|
|
29
|
+
- **One-line bootstrap per context** with cross-browser API wrapper
|
|
30
|
+
- **Cross-context auth sync**: sign-in in one tab is reflected in all open contexts (no `chrome.storage` needed)
|
|
31
|
+
- **Auto-translation** to 16 languages via Claude CLI on every build
|
|
32
|
+
- **Four-layer test framework**: build / background / view / boot — real Chromium, real MV3 service worker, real consumer extensions
|
|
33
|
+
- **Multi-browser packaging + auto-publish** to Chrome / Firefox / Edge stores from one command
|
|
34
|
+
- **Theme system**: Bootstrap 5 + Classy (custom design system), or roll your own
|
|
35
|
+
- **SCSS load paths**: `@use 'browser-extension-manager'` / `@use 'theme'` Just Work — no relative-path hell
|
|
30
36
|
|
|
31
37
|
## 🚀 Getting started
|
|
32
|
-
|
|
38
|
+
|
|
39
|
+
1. [Create a repo](https://github.com/itw-creative-works/ultimate-browser-extension/generate) from the **Ultimate Browser Extension** template (or `npm i browser-extension-manager` in an existing project).
|
|
33
40
|
2. Clone the repo to your local machine.
|
|
34
|
-
3.
|
|
41
|
+
3. Set up + run:
|
|
42
|
+
```bash
|
|
43
|
+
npm install
|
|
44
|
+
npx bxm setup
|
|
45
|
+
npm start
|
|
46
|
+
```
|
|
47
|
+
4. Open Chrome and navigate to `chrome://extensions`.
|
|
48
|
+
5. Enable **Developer mode**.
|
|
49
|
+
6. Click **Load unpacked** and select the `packaged/chromium/raw` folder in your project.
|
|
50
|
+
7. Your extension is loaded and live-reloads on source changes.
|
|
51
|
+
|
|
52
|
+
## 📦 Sync with the template
|
|
53
|
+
|
|
54
|
+
Run `npx bxm setup` again to pull the latest framework defaults. Files you've edited are preserved; only missing or framework-owned files update.
|
|
55
|
+
|
|
56
|
+
## 🧪 Testing
|
|
57
|
+
|
|
58
|
+
BXM ships a built-in four-layer test framework. Write tests under `test/<layer>/*.test.js` and run with:
|
|
59
|
+
|
|
35
60
|
```bash
|
|
36
|
-
|
|
37
|
-
npx bxm
|
|
38
|
-
|
|
61
|
+
npx bxm test # all layers
|
|
62
|
+
npx bxm test --layer build # build layer only (plain Node, fast)
|
|
63
|
+
npx bxm test --layer boot # real-Chromium end-to-end test
|
|
64
|
+
npx bxm test --integration # also run integration suites against REAL external services (Firebase, etc.)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Tests run against the **real** harness — a real MV3 service worker, a real Chromium tab, the real packaged extension. **Never mock** (`chrome`, the Manager, contexts are all real); only pure, I/O-free functions are called directly. Real external APIs are gated behind `--integration` (skipped in-source otherwise, never mocked).
|
|
68
|
+
|
|
69
|
+
Test files use Jest-compatible matchers:
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
// test/build/manifest.test.js
|
|
73
|
+
const Manager = require('browser-extension-manager/build');
|
|
74
|
+
|
|
75
|
+
module.exports = {
|
|
76
|
+
layer: 'build',
|
|
77
|
+
description: 'manifest is valid MV3',
|
|
78
|
+
run: (ctx) => {
|
|
79
|
+
const m = Manager.getManifest();
|
|
80
|
+
ctx.expect(m.manifest_version).toBe(3);
|
|
81
|
+
ctx.expect(m.permissions).toContain('storage');
|
|
82
|
+
},
|
|
83
|
+
};
|
|
39
84
|
```
|
|
40
|
-
4. Open your browser and navigate to `chrome://extensions` (or the equivalent for your browser).
|
|
41
|
-
5. Enable **Developer mode**.
|
|
42
|
-
6. Click on **Load unpacked** and select the `dist` folder in your project directory.
|
|
43
|
-
7. Your extension should now be loaded and ready to use!
|
|
44
85
|
|
|
45
|
-
|
|
46
|
-
|
|
86
|
+
Full guide: [docs/test-framework.md](docs/test-framework.md). End-to-end "did my packaged extension actually boot in Chrome?" tests: [docs/test-boot-layer.md](docs/test-boot-layer.md).
|
|
87
|
+
|
|
88
|
+
## 🌐 Auto-translation
|
|
89
|
+
|
|
90
|
+
When you run `npm run build`, BXM auto-translates `src/_locales/en/messages.json` to 16 languages via Claude CLI:
|
|
47
91
|
|
|
48
|
-
## 🌐 Automatic Translation
|
|
49
|
-
When you run `npm run build`, BXM automatically translates your `src/_locales/en/messages.json` to 16 languages using Claude CLI:
|
|
50
92
|
`zh`, `es`, `hi`, `ar`, `pt`, `ru`, `ja`, `de`, `fr`, `ko`, `ur`, `id`, `bn`, `tl`, `vi`, `it`
|
|
51
93
|
|
|
52
|
-
Only missing translations are generated
|
|
94
|
+
Only missing translations are generated — existing translations are preserved. Full guide: [docs/translations.md](docs/translations.md).
|
|
53
95
|
|
|
54
96
|
## 🌎 Publishing your extension
|
|
55
97
|
|
|
56
|
-
### Manual
|
|
57
|
-
1. Run `npm run build` in Terminal to build your extension for production.
|
|
58
|
-
2. Upload the `.zip` file to the browser's extension store.
|
|
98
|
+
### Manual upload
|
|
59
99
|
|
|
60
|
-
|
|
61
|
-
|
|
100
|
+
```bash
|
|
101
|
+
npm run build
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Upload the `.zip` files under `packaged/<browser>/` to each browser's extension store.
|
|
105
|
+
|
|
106
|
+
### Automatic publishing
|
|
62
107
|
|
|
63
108
|
```bash
|
|
64
109
|
BXM_IS_PUBLISH=true npm run build
|
|
65
110
|
```
|
|
66
111
|
|
|
67
|
-
|
|
112
|
+
Add store credentials to your `.env`:
|
|
68
113
|
|
|
69
114
|
```bash
|
|
70
115
|
# Chrome Web Store
|
|
71
|
-
CHROME_EXTENSION_ID="
|
|
72
|
-
CHROME_CLIENT_ID="
|
|
73
|
-
CHROME_CLIENT_SECRET="
|
|
74
|
-
CHROME_REFRESH_TOKEN="
|
|
116
|
+
CHROME_EXTENSION_ID="..."
|
|
117
|
+
CHROME_CLIENT_ID="..."
|
|
118
|
+
CHROME_CLIENT_SECRET="..."
|
|
119
|
+
CHROME_REFRESH_TOKEN="..."
|
|
75
120
|
|
|
76
121
|
# Firefox Add-ons
|
|
77
|
-
FIREFOX_EXTENSION_ID="
|
|
78
|
-
FIREFOX_API_KEY="
|
|
79
|
-
FIREFOX_API_SECRET="
|
|
122
|
+
FIREFOX_EXTENSION_ID="..."
|
|
123
|
+
FIREFOX_API_KEY="..."
|
|
124
|
+
FIREFOX_API_SECRET="..."
|
|
80
125
|
|
|
81
126
|
# Microsoft Edge Add-ons
|
|
82
|
-
EDGE_PRODUCT_ID="
|
|
83
|
-
EDGE_CLIENT_ID="
|
|
84
|
-
EDGE_API_KEY="
|
|
127
|
+
EDGE_PRODUCT_ID="..."
|
|
128
|
+
EDGE_CLIENT_ID="..."
|
|
129
|
+
EDGE_API_KEY="..."
|
|
85
130
|
```
|
|
86
131
|
|
|
87
|
-
Only stores with configured credentials
|
|
132
|
+
Only stores with configured credentials get published to. Full guide: [docs/publishing.md](docs/publishing.md).
|
|
88
133
|
|
|
89
134
|
## 🔐 Authentication
|
|
90
135
|
|
|
91
|
-
BXM provides built-in authentication that syncs across all extension contexts (popup, options,
|
|
92
|
-
|
|
93
|
-
### How It Works
|
|
136
|
+
BXM provides built-in cross-context authentication that syncs across all extension contexts (popup, options, sidepanel, pages, background) without using `chrome.storage`.
|
|
94
137
|
|
|
95
|
-
**Background.js is the source of truth.** Auth syncs via messaging
|
|
138
|
+
**Background.js is the source of truth.** Auth syncs via messaging — sign-in / sign-out events propagate across all open contexts, and new contexts handshake with background on load.
|
|
96
139
|
|
|
97
|
-
|
|
98
|
-
- **Context load**: Each context compares its UID with background's UID on load; syncs if different
|
|
99
|
-
- **Sign-out**: User clicks `.auth-signout-btn` → context signs out → notifies background → background broadcasts sign-out to all contexts
|
|
100
|
-
|
|
101
|
-
### Required Setup
|
|
140
|
+
### Setup
|
|
102
141
|
|
|
103
142
|
1. Add `authDomain` to your Firebase config in `config/browser-extension-manager.json`
|
|
104
|
-
2. Add `tabs` permission to `src/manifest.json`
|
|
143
|
+
2. Add `tabs` permission to `src/manifest.json`
|
|
144
|
+
|
|
145
|
+
### Auth button classes
|
|
105
146
|
|
|
106
|
-
|
|
147
|
+
Add these CSS classes to HTML elements for declarative auth UI:
|
|
107
148
|
|
|
108
149
|
| Class | Action |
|
|
109
|
-
|
|
110
|
-
| `.auth-signin-btn` | Opens `/token` page on website |
|
|
111
|
-
| `.auth-signout-btn` | Signs out via Web Manager |
|
|
112
|
-
| `.auth-account-btn` | Opens `/account` page on website |
|
|
150
|
+
|---|---|
|
|
151
|
+
| `.auth-signin-btn` | Opens `/token` page on your website |
|
|
152
|
+
| `.auth-signout-btn` | Signs out via Web Manager (broadcasts to all contexts) |
|
|
153
|
+
| `.auth-account-btn` | Opens `/account` page on your website |
|
|
113
154
|
|
|
114
|
-
### Example
|
|
115
155
|
```html
|
|
116
156
|
<button class="btn auth-signin-btn" data-wm-bind="@show !auth.user">Sign In</button>
|
|
117
157
|
|
|
118
158
|
<div data-wm-bind="@show auth.user" hidden>
|
|
159
|
+
<img data-wm-bind="@attr src auth.user.photoURL">
|
|
119
160
|
<span data-wm-bind="@text auth.user.displayName">User</span>
|
|
120
161
|
<button class="auth-signout-btn">Sign Out</button>
|
|
121
162
|
</div>
|
|
122
163
|
```
|
|
123
164
|
|
|
124
|
-
|
|
125
|
-
- `@show auth.user` / `@show !auth.user` - Show/hide based on auth state
|
|
126
|
-
- `@text auth.user.displayName` / `@text auth.user.email` - Display user info
|
|
127
|
-
- `@attr src auth.user.photoURL` - Set avatar image
|
|
165
|
+
Full guide: [docs/auth.md](docs/auth.md).
|
|
128
166
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
167
|
+
## 📚 Documentation
|
|
168
|
+
|
|
169
|
+
In-depth docs for every subsystem live in [docs/](docs/). See [CLAUDE.md](CLAUDE.md) for the architecture overview + table of contents.
|
|
170
|
+
|
|
171
|
+
## 🧰 Sister projects
|
|
172
|
+
|
|
173
|
+
- [Electron Manager (EM)](https://github.com/itw-creative-works/electron-manager) — same patterns, but for Electron desktop apps
|
|
174
|
+
- [Ultimate Jekyll Manager (UJM)](https://github.com/itw-creative-works/ultimate-jekyll-manager) — Jekyll static-site framework
|
|
175
|
+
- [Backend Manager (BEM)](https://github.com/itw-creative-works/backend-manager) — Firebase Functions backend framework
|
package/dist/background.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Libraries
|
|
2
2
|
import extension from './lib/extension.js';
|
|
3
3
|
import LoggerLite from './lib/logger-lite.js';
|
|
4
|
+
import { attachTo as attachModeHelpers } from './utils/mode-helpers.js';
|
|
4
5
|
|
|
5
6
|
// Firebase (static imports - dynamic import() doesn't work in service workers with webpack chunking)
|
|
6
7
|
import { initializeApp, getApp } from 'firebase/app';
|
|
@@ -512,7 +513,8 @@ class Manager {
|
|
|
512
513
|
|
|
513
514
|
// Setup livereload
|
|
514
515
|
setupLiveReload() {
|
|
515
|
-
//
|
|
516
|
+
// Dev-only feature — skip in testing and production (environment is one of
|
|
517
|
+
// 'development' | 'testing' | 'production').
|
|
516
518
|
if (this.environment !== 'development') return;
|
|
517
519
|
|
|
518
520
|
// Get port from config or use default
|
|
@@ -660,5 +662,8 @@ function setupGlobalHandlers() {
|
|
|
660
662
|
});
|
|
661
663
|
}
|
|
662
664
|
|
|
665
|
+
// Cross-context helpers — Manager.isTesting() / isDevelopment() / etc.
|
|
666
|
+
attachModeHelpers(Manager);
|
|
667
|
+
|
|
663
668
|
// Export
|
|
664
669
|
export default Manager;
|
package/dist/build.js
CHANGED
|
@@ -91,13 +91,8 @@ Manager.actLikeProduction = function () {
|
|
|
91
91
|
}
|
|
92
92
|
Manager.prototype.actLikeProduction = Manager.actLikeProduction;
|
|
93
93
|
|
|
94
|
-
// getEnvironment
|
|
95
|
-
|
|
96
|
-
return Manager.isBuildMode()
|
|
97
|
-
? 'production'
|
|
98
|
-
: 'development';
|
|
99
|
-
}
|
|
100
|
-
Manager.prototype.getEnvironment = Manager.getEnvironment;
|
|
94
|
+
// getEnvironment() is the SSOT and lives in src/utils/mode-helpers.js (alongside the is*()
|
|
95
|
+
// family). It's mixed onto the Manager via the attachTo() call below, same as in EM/UJM.
|
|
101
96
|
|
|
102
97
|
// getManifest: requires and parses config.yml
|
|
103
98
|
Manager.getManifest = function () {
|
|
@@ -196,5 +191,8 @@ Manager.logMemory = function (logger, label) {
|
|
|
196
191
|
};
|
|
197
192
|
Manager.prototype.logMemory = Manager.logMemory;
|
|
198
193
|
|
|
194
|
+
// Cross-context helpers — Manager.isTesting() / isDevelopment() / etc.
|
|
195
|
+
require('./utils/mode-helpers.js').attachTo(Manager);
|
|
196
|
+
|
|
199
197
|
// Export
|
|
200
198
|
module.exports = Manager;
|
package/dist/cli.js
CHANGED
package/dist/commands/install.js
CHANGED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Libraries
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const Manager = new (require('../build.js'));
|
|
5
|
+
const logger = Manager.logger('test');
|
|
6
|
+
const { run } = require('../test/runner.js');
|
|
7
|
+
|
|
8
|
+
module.exports = async function (options) {
|
|
9
|
+
const layer = options.layer || 'all';
|
|
10
|
+
const filter = options.filter || null;
|
|
11
|
+
const reporter = options.reporter || 'pretty';
|
|
12
|
+
const integration = options.integration === true || options.integration === 'true';
|
|
13
|
+
|
|
14
|
+
if (integration) {
|
|
15
|
+
process.env.BXM_TEST_INTEGRATION = '1';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Canonical signal — every Manager picks this up via isTesting().
|
|
19
|
+
process.env.BXM_TEST_MODE = 'true';
|
|
20
|
+
|
|
21
|
+
// When BXM itself runs its own boot-layer tests (the cwd's package.json is
|
|
22
|
+
// BXM's package.json), there's no real consumer extension to target. Point
|
|
23
|
+
// the boot runner at the fixture under dist/test/fixtures/consumer-extension
|
|
24
|
+
// unless the caller has already set BXM_TEST_BOOT_PROJECT explicitly.
|
|
25
|
+
if (!process.env.BXM_TEST_BOOT_PROJECT) {
|
|
26
|
+
try {
|
|
27
|
+
const cwdPkg = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8'));
|
|
28
|
+
if (cwdPkg.name === 'browser-extension-manager') {
|
|
29
|
+
process.env.BXM_TEST_BOOT_PROJECT = path.join(__dirname, '..', 'test', 'fixtures', 'consumer-extension');
|
|
30
|
+
}
|
|
31
|
+
} catch (_) { /* no package.json — leave unset */ }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (reporter !== 'json') {
|
|
35
|
+
logger.log(`Running tests (layer=${layer}${filter ? ` filter="${filter}"` : ''}${integration ? ' +integration' : ''})`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const result = await run({ layer, filter, reporter });
|
|
39
|
+
|
|
40
|
+
if (reporter === 'json') {
|
|
41
|
+
// Final machine-readable summary.
|
|
42
|
+
process.stdout.write(JSON.stringify({
|
|
43
|
+
event: 'summary',
|
|
44
|
+
passed: result.passed,
|
|
45
|
+
failed: result.failed,
|
|
46
|
+
skipped: result.skipped,
|
|
47
|
+
total: result.passed + result.failed + result.skipped,
|
|
48
|
+
}) + '\n');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (result.failed > 0) {
|
|
52
|
+
process.exitCode = 1;
|
|
53
|
+
throw new Error(`${result.failed} test(s) failed`);
|
|
54
|
+
}
|
|
55
|
+
};
|
package/dist/content.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import extension from './lib/extension.js';
|
|
3
3
|
import LoggerLite from './lib/logger-lite.js';
|
|
4
4
|
import Affiliatizer from './lib/affiliatizer.js';
|
|
5
|
+
import { attachTo as attachModeHelpers } from './utils/mode-helpers.js';
|
|
5
6
|
|
|
6
7
|
// Class
|
|
7
8
|
class Manager {
|
|
@@ -28,5 +29,8 @@ class Manager {
|
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
// Cross-context helpers — Manager.isTesting() / isDevelopment() / etc.
|
|
33
|
+
attachModeHelpers(Manager);
|
|
34
|
+
|
|
31
35
|
// Export
|
|
32
36
|
export default Manager;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# CHANGELOG
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
6
|
+
|
|
7
|
+
## Changelog Categories
|
|
8
|
+
|
|
9
|
+
- `BREAKING` for breaking changes.
|
|
10
|
+
- `Added` for new features.
|
|
11
|
+
- `Changed` for changes in existing functionality.
|
|
12
|
+
- `Deprecated` for soon-to-be removed features.
|
|
13
|
+
- `Removed` for now removed features.
|
|
14
|
+
- `Fixed` for any bug fixes.
|
|
15
|
+
- `Security` in case of vulnerabilities.
|
package/dist/defaults/CLAUDE.md
CHANGED
|
@@ -1,8 +1,84 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
1
|
+
# ========== Default Values ==========
|
|
2
|
+
# Browser Extension Manager (BXM) — consumer project
|
|
3
3
|
|
|
4
|
-
##
|
|
5
|
-
You should have a full understanding of Browser Extension Manager before editing this project, which can be found at: node_modules/browser-extension-manager/CLAUDE.md
|
|
4
|
+
## Framework
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
This project consumes **Browser Extension Manager** (BXM) — a comprehensive framework for building modern cross-browser extensions (Chrome, Firefox, Edge, Opera, Brave). BXM provides one-line bootstrap per extension context, a component-based architecture (view + styles + script per context), a multi-browser build/release pipeline that produces store-uploadable zips, cross-context auth synchronization, and a built-in four-layer test framework.
|
|
7
|
+
|
|
8
|
+
## 🚨 READ THE FRAMEWORK DOCS FIRST
|
|
9
|
+
|
|
10
|
+
**Before doing ANY work on this codebase, Claude MUST read the framework documentation — that is where the architecture, conventions, APIs, and gotchas live. Skipping these will result in solutions that conflict with framework patterns.**
|
|
11
|
+
|
|
12
|
+
**Required reading:**
|
|
13
|
+
- **`node_modules/browser-extension-manager/CLAUDE.md`** — top-level overview + index
|
|
14
|
+
- **`node_modules/browser-extension-manager/docs/`** — subsystem deep references (read the relevant ones for the task at hand)
|
|
15
|
+
|
|
16
|
+
## 🚨 READ WEB-MANAGER TOO
|
|
17
|
+
|
|
18
|
+
**BXM ships `web-manager` as a runtime singleton across every extension context** (background service worker, popup, options, sidepanel, content scripts) — it powers auth, Firebase, reactive `data-wm-bind` directives, analytics, error tracking, and utilities (`escapeHTML`, etc.). Any task that touches auth flows, Firestore reads/writes, subscription resolution, push notifications, or DOM bindings means you are working with web-manager as much as with BXM.
|
|
19
|
+
|
|
20
|
+
**Required reading:**
|
|
21
|
+
- **`node_modules/web-manager/CLAUDE.md`** — top-level overview + index
|
|
22
|
+
- **`node_modules/web-manager/docs/`** — module deep references (Auth, Bindings, Firestore, Notifications, etc.)
|
|
23
|
+
|
|
24
|
+
## Quick start
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm start # dev with live reload (gulp → webpack → serve)
|
|
28
|
+
npm run build # production build → dist/ + packaged/<browser>/raw/ + .zip per browser
|
|
29
|
+
BXM_IS_PUBLISH=true npm run build # build + auto-upload to Chrome / Firefox / Edge stores
|
|
30
|
+
npx mgr test # run framework + project test suites
|
|
31
|
+
npx mgr install dev # use LOCAL browser-extension-manager source (to test framework edits)
|
|
32
|
+
npx mgr install live # restore the published browser-extension-manager from npm
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
> Editing the BXM framework source while working here? Run `npx mgr install dev` so this project picks up your uncommitted framework changes (it otherwise uses its installed `node_modules/browser-extension-manager`). Run `npx mgr install live` to switch back.
|
|
36
|
+
|
|
37
|
+
Load the unpacked extension in Chrome: point chrome://extensions → "Load unpacked" at `packaged/chromium/raw/`.
|
|
38
|
+
|
|
39
|
+
## Where things live
|
|
40
|
+
|
|
41
|
+
- `config/browser-extension-manager.json` — JSON5 config: brand, manifest overrides, build settings, theme. `Manager.getConfig()` reads this.
|
|
42
|
+
- `config/messages.json` — i18n source. Auto-translated to 16 languages at build time via the Claude CLI (only missing keys regenerated).
|
|
43
|
+
- `config/description.md` — store-listing description (used by the publish step).
|
|
44
|
+
- `src/manifest.json` — extension manifest. BXM merges its defaults in at build time; you only need to declare what's specific to your extension.
|
|
45
|
+
- `src/views/<context>/index.html` — per-context HTML (popup / options / sidepanel / pages).
|
|
46
|
+
- `src/assets/js/components/<context>/index.js` — per-context script entry. One-line bootstrap of `browser-extension-manager/<context>`.
|
|
47
|
+
- `src/assets/css/components/<context>/index.scss` — per-context styles.
|
|
48
|
+
- `src/assets/js/components/background.js` — MV3 service worker entry. Source of truth for auth + messaging.
|
|
49
|
+
- `src/_locales/en/messages.json` — Chrome `__MSG_*__` placeholders (auto-translated to 16 langs at build).
|
|
50
|
+
- `hooks/build/{pre,post}.js` — optional lifecycle hooks.
|
|
51
|
+
- `test/**/*.js` — your project test suites (framework auto-runs them alongside its own).
|
|
52
|
+
|
|
53
|
+
## Per-context imports
|
|
54
|
+
|
|
55
|
+
```js
|
|
56
|
+
// src/assets/js/components/popup/index.js
|
|
57
|
+
import Manager from 'browser-extension-manager/popup';
|
|
58
|
+
await new Manager().initialize();
|
|
59
|
+
|
|
60
|
+
// src/assets/js/components/background.js (service worker)
|
|
61
|
+
import Manager from 'browser-extension-manager/background';
|
|
62
|
+
await new Manager().initialize();
|
|
63
|
+
|
|
64
|
+
// Same shape for options / sidepanel / content / page / offscreen
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Available APIs at runtime
|
|
68
|
+
|
|
69
|
+
After `initialize()`, every Manager exposes:
|
|
70
|
+
- `manager.extension` — cross-browser `chrome.*` / `browser.*` / `window.*` wrapper
|
|
71
|
+
- `manager.logger` — timestamped per-context logger
|
|
72
|
+
- `manager.webManager` — Web Manager singleton (Firebase, auth, analytics, reactive `data-wm-bind` directives)
|
|
73
|
+
- `manager.messenger` — `chrome.runtime.onMessage` listener wired automatically
|
|
74
|
+
- `manager.isDevelopment()` / `isProduction()` / `isTesting()` / `getVersion()` — cross-context helpers. `getEnvironment()` returns `'development' | 'testing' | 'production'` (mutually exclusive; testing wins). Gate side effects on the intentional check (`isProduction()` for prod-only; `isDevelopment() || isTesting()` for local-or-test) — never `!isDevelopment()`.
|
|
75
|
+
|
|
76
|
+
Auth UI is declarative — add `.auth-signin-btn` / `.auth-signout-btn` / `.auth-account-btn` to buttons; BXM wires them. Show/hide based on auth state via `data-wm-bind="@show auth.user"`.
|
|
77
|
+
|
|
78
|
+
<!-- Everything above this marker is owned by the framework and rewritten on every `npx mgr setup`. Add your project-specific notes below — they are preserved across setups. -->
|
|
79
|
+
|
|
80
|
+
# ========== Custom Values ==========
|
|
81
|
+
|
|
82
|
+
## Project-specific notes
|
|
83
|
+
|
|
84
|
+
Add anything specific to THIS project here. Edits below this line are preserved across `npx mgr setup` runs.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Project docs
|
|
2
|
+
|
|
3
|
+
Per-subsystem deep references live here. Keep `CLAUDE.md` short — it should read as a **table of contents** that points at files in this directory.
|
|
4
|
+
|
|
5
|
+
## Pattern
|
|
6
|
+
|
|
7
|
+
When you find yourself adding more than a paragraph to `CLAUDE.md`, create a new `docs/<topic>.md` instead and link to it from `CLAUDE.md`. Goal: the project's `CLAUDE.md` stays under ~250 lines.
|
|
8
|
+
|
|
9
|
+
Examples of good `docs/*.md` topics:
|
|
10
|
+
- Subsystem deep-dives (one per area of the codebase)
|
|
11
|
+
- Architectural decisions / "why we built it this way"
|
|
12
|
+
- Defaults tables, behavior matrices, edge cases
|
|
13
|
+
- Setup walkthroughs that don't belong in `README.md`
|
|
14
|
+
|
|
15
|
+
## See also
|
|
16
|
+
|
|
17
|
+
The framework's own docs follow this same pattern — browse `node_modules/browser-extension-manager/docs/` for the canonical examples.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Project tests
|
|
2
|
+
|
|
3
|
+
Drop your project test suites here. The framework auto-runs them alongside its own when you run `npx mgr test`.
|
|
4
|
+
|
|
5
|
+
## Layers
|
|
6
|
+
|
|
7
|
+
Match the framework's four layers — Browser Extension Manager's test runner discovers files by the directory they sit in:
|
|
8
|
+
|
|
9
|
+
| Directory | Runtime | Use for |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| `test/build/` | Plain Node | Build-time logic, manifest validation, pure utilities |
|
|
12
|
+
| `test/background/` | MV3 service worker context | Background messaging, auth source-of-truth, alarms |
|
|
13
|
+
| `test/view/` | Popup / options / sidepanel page | DOM, view-side controllers, `data-wm-bind` directives |
|
|
14
|
+
| `test/boot/` | Consumer's actual built extension | End-to-end smoke tests (does the extension load, does the background register, do views render) |
|
|
15
|
+
|
|
16
|
+
## Quick example
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
// test/build/my-feature.test.js
|
|
20
|
+
const assert = require('browser-extension-manager/test/assert');
|
|
21
|
+
|
|
22
|
+
module.exports = {
|
|
23
|
+
'my feature does the thing': async () => {
|
|
24
|
+
const result = await doTheThing();
|
|
25
|
+
assert.equal(result, 'expected');
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## See also
|
|
31
|
+
|
|
32
|
+
`node_modules/browser-extension-manager/docs/test-framework.md` — full reference for the test framework (layers, assert API, fixtures, runner internals).
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test lifecycle hook for this project. Runs once before any suite (not a test itself).
|
|
3
|
+
* See browser-extension-manager/docs/test-framework.md → "test/_init.js".
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
module.exports = ({ projectRoot }) => ({
|
|
7
|
+
// Seed any fixture a suite needs before it runs.
|
|
8
|
+
async setup() {
|
|
9
|
+
},
|
|
10
|
+
});
|
|
@@ -66,6 +66,15 @@ const FILE_MAP = {
|
|
|
66
66
|
mergeLines: true,
|
|
67
67
|
},
|
|
68
68
|
|
|
69
|
+
// Consumer CLAUDE.md uses the same marker-based merge as .env/.gitignore.
|
|
70
|
+
// Framework owns the Default section; consumer owns everything below the Custom marker.
|
|
71
|
+
// Must come AFTER `**/*.md` (which sets overwrite: false) — `getFileOptions` does
|
|
72
|
+
// last-match-wins, so this rule's `mergeLines: true` activates the merge path even though
|
|
73
|
+
// the catch-all would otherwise skip.
|
|
74
|
+
'CLAUDE.md': {
|
|
75
|
+
mergeLines: true,
|
|
76
|
+
},
|
|
77
|
+
|
|
69
78
|
// Config files
|
|
70
79
|
'config/browser-extension-manager.json': {
|
|
71
80
|
overwrite: true,
|
|
@@ -270,8 +279,12 @@ function mergeLineBasedFiles(existingContent, newContent, fileName) {
|
|
|
270
279
|
result.push(DEFAULT_SECTION_MARKER);
|
|
271
280
|
result.push(...mergedDefaultSection);
|
|
272
281
|
|
|
273
|
-
// Add custom section
|
|
274
|
-
|
|
282
|
+
// Add custom section. Insert a single blank line before the marker, but only if the
|
|
283
|
+
// merged default doesn't already end with one — otherwise we'd accumulate an extra blank
|
|
284
|
+
// line on every merge, breaking idempotency after the first re-run.
|
|
285
|
+
if (mergedDefaultSection.length === 0 || mergedDefaultSection[mergedDefaultSection.length - 1].trim() !== '') {
|
|
286
|
+
result.push('');
|
|
287
|
+
}
|
|
275
288
|
result.push(CUSTOM_SECTION_MARKER);
|
|
276
289
|
|
|
277
290
|
// First add any user lines that were in default section (moved to custom)
|
package/dist/offscreen.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Libraries
|
|
2
2
|
import extension from './lib/extension.js';
|
|
3
3
|
import LoggerLite from './lib/logger-lite.js';
|
|
4
|
+
import { attachTo as attachModeHelpers } from './utils/mode-helpers.js';
|
|
4
5
|
|
|
5
6
|
// Class
|
|
6
7
|
class Manager {
|
|
@@ -23,5 +24,8 @@ class Manager {
|
|
|
23
24
|
}
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
// Cross-context helpers — Manager.isTesting() / isDevelopment() / etc.
|
|
28
|
+
attachModeHelpers(Manager);
|
|
29
|
+
|
|
26
30
|
// Export
|
|
27
31
|
export default Manager;
|
package/dist/options.js
CHANGED
|
@@ -3,6 +3,7 @@ import webManager from 'web-manager';
|
|
|
3
3
|
import extension from './lib/extension.js';
|
|
4
4
|
import LoggerLite from './lib/logger-lite.js';
|
|
5
5
|
import { syncWithBackground, setupAuthBroadcastListener, setupSignOutListener, setupAuthEventListeners, openAuthPage as openAuthPageHelper } from './lib/auth-helpers.js';
|
|
6
|
+
import { attachTo as attachModeHelpers } from './utils/mode-helpers.js';
|
|
6
7
|
|
|
7
8
|
// Import theme (exposes Bootstrap to window.bootstrap)
|
|
8
9
|
import '__theme__/_theme.js';
|
|
@@ -59,5 +60,8 @@ class Manager {
|
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
|
|
63
|
+
// Cross-context helpers — Manager.isTesting() / isDevelopment() / etc.
|
|
64
|
+
attachModeHelpers(Manager);
|
|
65
|
+
|
|
62
66
|
// Export
|
|
63
67
|
export default Manager;
|
package/dist/page.js
CHANGED
|
@@ -3,6 +3,7 @@ import webManager from 'web-manager';
|
|
|
3
3
|
import extension from './lib/extension.js';
|
|
4
4
|
import LoggerLite from './lib/logger-lite.js';
|
|
5
5
|
import { syncWithBackground, setupAuthBroadcastListener, setupSignOutListener, setupAuthEventListeners, openAuthPage as openAuthPageHelper } from './lib/auth-helpers.js';
|
|
6
|
+
import { attachTo as attachModeHelpers } from './utils/mode-helpers.js';
|
|
6
7
|
|
|
7
8
|
// Import theme (exposes Bootstrap to window.bootstrap)
|
|
8
9
|
import '__theme__/_theme.js';
|
|
@@ -59,5 +60,8 @@ class Manager {
|
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
|
|
63
|
+
// Cross-context helpers — Manager.isTesting() / isDevelopment() / etc.
|
|
64
|
+
attachModeHelpers(Manager);
|
|
65
|
+
|
|
62
66
|
// Export
|
|
63
67
|
export default Manager;
|