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.
Files changed (49) hide show
  1. package/README.md +112 -70
  2. package/dist/background.js +6 -1
  3. package/dist/build.js +5 -7
  4. package/dist/cli.js +1 -0
  5. package/dist/commands/install.js +1 -1
  6. package/dist/commands/test.js +55 -0
  7. package/dist/content.js +4 -0
  8. package/dist/defaults/CHANGELOG.md +15 -0
  9. package/dist/defaults/CLAUDE.md +82 -6
  10. package/dist/defaults/docs/README.md +17 -0
  11. package/dist/defaults/test/README.md +32 -0
  12. package/dist/defaults/test/_init.js +10 -0
  13. package/dist/gulp/tasks/defaults.js +15 -2
  14. package/dist/offscreen.js +4 -0
  15. package/dist/options.js +4 -0
  16. package/dist/page.js +4 -0
  17. package/dist/popup.js +4 -0
  18. package/dist/sidepanel.js +4 -0
  19. package/dist/test/assert.js +120 -0
  20. package/dist/test/fixtures/consumer-extension/dist/background.js +15 -0
  21. package/dist/test/fixtures/consumer-extension/dist/manifest.json +20 -0
  22. package/dist/test/fixtures/consumer-extension/dist/options.html +10 -0
  23. package/dist/test/fixtures/consumer-extension/dist/popup.html +11 -0
  24. package/dist/test/harness/extension/background.js +26 -0
  25. package/dist/test/harness/extension/manifest.json +27 -0
  26. package/dist/test/harness/extension/options.html +12 -0
  27. package/dist/test/harness/extension/popup.html +12 -0
  28. package/dist/test/harness/extension/sidepanel.html +12 -0
  29. package/dist/test/index.js +63 -0
  30. package/dist/test/runner.js +469 -0
  31. package/dist/test/runners/boot.js +201 -0
  32. package/dist/test/runners/chromium.js +399 -0
  33. package/dist/test/suites/background/messaging.test.js +42 -0
  34. package/dist/test/suites/background/storage.test.js +44 -0
  35. package/dist/test/suites/background/sw-context.test.js +47 -0
  36. package/dist/test/suites/boot/extension-loads.test.js +56 -0
  37. package/dist/test/suites/build/affiliatizer.test.js +63 -0
  38. package/dist/test/suites/build/cli.test.js +123 -0
  39. package/dist/test/suites/build/expect.test.js +47 -0
  40. package/dist/test/suites/build/exports.test.js +52 -0
  41. package/dist/test/suites/build/extension-fallback.test.js +41 -0
  42. package/dist/test/suites/build/logger-lite.test.js +59 -0
  43. package/dist/test/suites/build/manager.test.js +184 -0
  44. package/dist/test/suites/build/mode-helpers.test.js +135 -0
  45. package/dist/test/suites/view/options-and-sidepanel.test.js +14 -0
  46. package/dist/test/suites/view/popup-context.test.js +51 -0
  47. package/dist/test/suites/view/sidepanel.test.js +14 -0
  48. package/dist/utils/mode-helpers.js +117 -0
  49. 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/ultimate-browser-extension.svg">
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/ultimate-browser-extension.svg">
11
- <img src="https://img.shields.io/bundlephobia/min/ultimate-browser-extension.svg">
12
- <img src="https://img.shields.io/codeclimate/maintainability-percentage/itw-creative-works/ultimate-browser-extension.svg">
13
- <img src="https://img.shields.io/npm/dm/ultimate-browser-extension.svg">
14
- <img src="https://img.shields.io/node/v/ultimate-browser-extension.svg">
15
- <img src="https://img.shields.io/website/https/itwcreativeworks.com.svg">
16
- <img src="https://img.shields.io/github/license/itw-creative-works/ultimate-browser-extension.svg">
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/ultimate-browser-extension">NPM Module</a> | <a href="https://github.com/itw-creative-works/ultimate-browser-extension">GitHub Repo</a>
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>Ultimate Browser Extension</strong> is a template that helps you jumpstart your Jekyll sites and is fueled by an intuitive incorporation of npm, gulp, and is fully SEO optimized and blazingly fast.
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
- * **Build for Any Browser**: Export to Chrome, Firefox, Edge, and Opera.
29
- * **NPM & Gulp**: Fueled by an intuitive incorporation of npm and gulp.
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
- 1. [Create a repo](https://github.com/itw-creative-works/ultimate-browser-extension/generate) from the **Ultimate Browser Extension** template.
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. Run these command to get everything setup and sync'd!
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
- npm install
37
- npx bxm setup
38
- npm start
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
- ## 📦 How to sync with the template
46
- 1. Simply run `npx bxm setup` in Terminal to get all the latest updates from the **Ultimate Browser Extension template**.
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 - existing translations are preserved.
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 Upload
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
- ### Automatic Publishing
61
- BXM can automatically publish to Chrome, Firefox, and Edge stores when `BXM_IS_PUBLISH=true`:
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
- **Setup:** Add store credentials to your `.env` file:
112
+ Add store credentials to your `.env`:
68
113
 
69
114
  ```bash
70
115
  # Chrome Web Store
71
- CHROME_EXTENSION_ID="your-extension-id"
72
- CHROME_CLIENT_ID="your-client-id"
73
- CHROME_CLIENT_SECRET="your-client-secret"
74
- CHROME_REFRESH_TOKEN="your-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="your-extension-id"
78
- FIREFOX_API_KEY="your-api-key"
79
- FIREFOX_API_SECRET="your-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="your-product-id"
83
- EDGE_CLIENT_ID="your-client-id"
84
- EDGE_API_KEY="your-api-key"
127
+ EDGE_PRODUCT_ID="..."
128
+ EDGE_CLIENT_ID="..."
129
+ EDGE_API_KEY="..."
85
130
  ```
86
131
 
87
- Only stores with configured credentials will be published to.
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, pages, sidepanel, background).
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 (no storage).
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
- - **Sign-in**: User clicks `.auth-signin-btn` → opens `/token` page on website → website authenticates and redirects with token → background.js signs in and broadcasts to all open contexts
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` (for URL monitoring)
143
+ 2. Add `tabs` permission to `src/manifest.json`
144
+
145
+ ### Auth button classes
105
146
 
106
- ### Auth Button Classes
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
- ### Reactive Bindings
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
- <!-- ## ⛳️ Flags
130
- * `--test=false` - Coming soon
131
- ```bash
132
- npm start -- --test=false
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
@@ -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
- // Quit if not in dev mode
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: returns the environment based on the build mode
95
- Manager.getEnvironment = function () {
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
@@ -11,6 +11,7 @@ const ALIASES = {
11
11
  clean: ['-c', '--clean'],
12
12
  install: ['-i', 'i', '--install'],
13
13
  setup: ['-s', '--setup'],
14
+ test: ['-t', '--test'],
14
15
  version: ['-v', '--version'],
15
16
  };
16
17
 
@@ -17,7 +17,7 @@ module.exports = async function (options) {
17
17
 
18
18
  try {
19
19
  // Install production
20
- if (['prod', 'p', 'production'].includes(type)) {
20
+ if (['live', 'prod', 'p', 'production'].includes(type)) {
21
21
  // Log
22
22
  logger.log('Installing production...');
23
23
 
@@ -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.
@@ -1,8 +1,84 @@
1
- # Identity
2
- This is a browser extension that is "consuming" a browser extension template project called Browser Extension Manager (BXM)--a collection of components that can be used to build a browser extension quickly and efficiently.
1
+ # ========== Default Values ==========
2
+ # Browser Extension Manager (BXM) consumer project
3
3
 
4
- ## BXM Documentation
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
- ## Project-specific Notes
8
- Add any notes specific to this project here.
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
- result.push('');
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;