browser-extension-manager 1.6.0 → 1.7.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/CLAUDE.md ADDED
@@ -0,0 +1,268 @@
1
+ # Browser Extension Manager (BXM)
2
+
3
+ > **Note for contributors and Claude:** This file is the architectural overview — identity, top-level conventions, and a map to deep references. The **meat** (per-subsystem APIs, edge cases, behavior tables, defaults lists) lives in `docs/<topic>.md`. When extending or adding content, write it in the matching `docs/*.md` file and cross-link from here — do NOT inline it. If a topic doesn't have a doc yet, create one. Goal: keep this file under 250 lines.
4
+
5
+ > **Mirrored structure:** BEM, UJM, BXM, and EM CLAUDE.md files mirror each other — shared sections (Supply-Chain Security, Development Workflow, File Conventions, etc.) appear in the **same order at the same position** across all four. When adding a section that applies to multiple frameworks, insert it in the same spot in all of them.
6
+
7
+ ## Identity
8
+
9
+ Browser Extension Manager (BXM) is a comprehensive framework for building modern cross-browser extensions (Chrome, Firefox, Edge, Opera, Brave). Sister project to Electron Manager (EM) and Ultimate Jekyll Manager (UJM). Provides one-line-import bootstrap per extension context, a component-based architecture, a multi-browser build/release pipeline, auto-translation across 16 languages, cross-context auth synchronization, and a built-in four-layer test framework.
10
+
11
+ ## Recommended skills
12
+
13
+ - **`omega:bxm`** — router skill. Auto-loads on BXM-specific keywords (`manifest.json`, `extension popup`, `extension background`, `offscreen document`, `chrome extension`, etc.) and points back to this CLAUDE.md + `docs/` (the SSOT), carrying only Claude-workflow hard rules and process checklists.
14
+ - **`js:patterns`** — JavaScript/Node.js conventions: file structure, JSDoc, defensive coding (`?.` usage), template literals, `package.json` conventions. Auto-loads when creating new `.js` files or touching JS module structure.
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
+ ### For Consuming Projects
27
+
28
+ 1. `npm install browser-extension-manager --save-dev`
29
+ 2. `npx bxm setup` — scaffolds the project (copies `src/defaults/` into the project: `src/manifest.json`, `src/views/`, `src/assets/`, `config/browser-extension-manager.json`, etc.)
30
+ 3. `npm start` — dev (gulp → webpack → serve with live reload)
31
+ 4. `npm run build` — production build (compiles `dist/`, packages per-browser into `packaged/<browser>/raw/` + `.zip`)
32
+ 5. `BXM_IS_PUBLISH=true npm run build` — also uploads to Chrome / Firefox / Edge stores (see [docs/publishing.md](docs/publishing.md))
33
+ 6. `npx bxm test` — runs framework + project test suites
34
+ - `npx mgr test build/config` — bare path: run tests matching a path in BOTH sources
35
+ - `npx mgr test project:` — run ONLY consumer project tests (`project:<path>` to narrow)
36
+ - `npx mgr test mgr:` — run ONLY framework tests (`mgr:` is the universal cross-framework alias; `bxm:` / `framework:` are equivalent)
37
+ - `npx mgr test bxm:build/config` — run only framework tests matching a path
38
+ - The positional target selects which test FILES run (by source + path); `--filter=<substring>` is orthogonal — it matches test NAMES within them
39
+ - Output is teed (ANSI-stripped) to `<projectRoot>/logs/test.log`, truncated fresh each run — `cat logs/test.log` instead of scrolling scrollback
40
+ - Extended mode (off by default): `npx mgr test --extended` or `TEST_EXTENDED_MODE=true npx mgr test` opts into tests that hit REAL external services (Firebase via web-manager, push, network). `TEST_EXTENDED_MODE` is the shared, unprefixed name across BEM/BXM/UJM/EM; it propagates to every spawned test environment
41
+
42
+ To load the unpacked extension in Chrome: point chrome://extensions → "Load unpacked" at `packaged/chromium/raw/`.
43
+
44
+ ### For Framework Development (This Repository)
45
+
46
+ 1. `npm install`
47
+ 2. `npm start` — watch + compile `src/` → `dist/` via prepare-package
48
+ 3. Test in the **designated test consumer** — `../powertools-browser-extension` is BXM's consumer for validating framework changes end-to-end (exercise any consumer-level flow there freely: builds, tests, packaging, runtime). From inside it, run `npx mgr install dev` to swap BXM to this local repo — required whenever you edit the framework source and want the consumer to pick up the changes (the consumer otherwise keeps its installed `node_modules/browser-extension-manager`). Reverse with `npx mgr install live`.
49
+ 4. `npm test` — runs the framework's own suites
50
+
51
+ ## Architecture
52
+
53
+ ### Per-context Manager singletons
54
+
55
+ Each extension context has its own one-line bootstrap. Eight contexts total — see [docs/managers.md](docs/managers.md):
56
+
57
+ ```js
58
+ // src/assets/js/components/popup/index.js
59
+ import Manager from 'browser-extension-manager/popup';
60
+ await new Manager().initialize();
61
+
62
+ // src/assets/js/components/background.js (service worker)
63
+ import Manager from 'browser-extension-manager/background';
64
+ await new Manager().initialize();
65
+
66
+ // Same shape for options / sidepanel / content / page / offscreen
67
+ ```
68
+
69
+ After `initialize()`, the Manager exposes:
70
+ - `manager.extension` — cross-browser `chrome.*` / `browser.*` / `window.*` API wrapper ([docs/extension.md](docs/extension.md))
71
+ - `manager.logger` — timestamped per-context logger
72
+ - `manager.webManager` — Web Manager singleton (Firebase, auth, analytics, reactive bindings)
73
+ - `manager.messenger` — `chrome.runtime.onMessage` listener wired automatically
74
+ - `manager.isDevelopment() / isProduction() / isTesting() / getVersion()` — cross-context helpers ([docs/environment-detection.md](docs/environment-detection.md))
75
+
76
+ ### Component architecture
77
+
78
+ Extensions are organized around **components** — each a browser-extension context bundling a view, styles, and a script. Seven contexts × three parts each:
79
+
80
+ | Component | Runs in | When |
81
+ |---|---|---|
82
+ | `background` | MV3 service worker | Always — source of truth for auth + messaging |
83
+ | `popup` | Browser-action popup | When the user clicks the toolbar button |
84
+ | `options` | Standalone tab | When the user opens settings |
85
+ | `sidepanel` | Chrome side panel (114+) | When the user opens the side panel |
86
+ | `content` | Each visited web page | Injected by manifest `content_scripts` |
87
+ | `pages` | Custom extension page (dashboard, welcome) | Routed via `chrome.tabs.create` |
88
+ | `offscreen` | Offscreen document (Chrome 109+) | For WebSocket, DOM parsing, long-running SW-adjacent tasks |
89
+
90
+ Each component has three parts at conventional paths:
91
+ - View: `src/views/<component>/index.html`
92
+ - Styles: `src/assets/css/components/<component>/index.scss`
93
+ - Script: `src/assets/js/components/<component>/index.js`
94
+
95
+ Compiled output: `dist/views/<component>/index.html`, `dist/assets/css/components/<component>.bundle.css`, `dist/assets/js/components/<component>.bundle.js`. See [docs/components.md](docs/components.md).
96
+
97
+ ### Cross-context auth sync
98
+
99
+ Background.js is the source of truth for authentication. Other contexts compare their UID with background's on load and sync up — sign-ins / sign-outs broadcast across all open contexts via `chrome.runtime` messaging. No `chrome.storage` involved; Firebase persists per-context sessions in IndexedDB.
100
+
101
+ Three flows: sign-in (website `/token` redirect → broadcast), context-load (`bxm:syncAuth`), sign-out (`bxm:signOut` broadcast). Auth-button CSS classes (`.auth-signin-btn`, `.auth-signout-btn`, `.auth-account-btn`) wire UI without writing JS. Web-Manager reactive bindings (`data-wm-bind="@show auth.user"`) handle DOM state.
102
+
103
+ Required setup: `firebaseConfig.authDomain` in config, `tabs` permission in manifest. See [docs/auth.md](docs/auth.md).
104
+
105
+ ### Build system
106
+
107
+ `src/` (consumer) → `dist/` (intermediate, JSON5 manifest) → `packaged/<browser>/raw/` (strict-JSON manifest, Chrome-loadable) → `packaged/<browser>/<name>.zip` (store upload).
108
+
109
+ - **Gulp** auto-loads tasks from `src/gulp/tasks/` via `src/gulp/main.js`. Tasks: `defaults`, `distribute`, `sass`, `webpack`, `html`, `icons`, `translate`, `package`, `serve`, `audit`.
110
+ - **Webpack** — bundles each `src/assets/js/components/<name>/index.js` with Babel transpilation. Custom `__theme__` alias resolves to the active theme. Template-replacement plugin substitutes `%%% version %%%` / `%%% brand.name %%%` / etc.
111
+ - **Sass** — load-path resolution lets consumer SCSS `@use 'browser-extension-manager'` / `@use 'theme'` / `@use 'components/popup'` without long relative paths. See [docs/css.md](docs/css.md).
112
+ - **HTML templating** — two-pass `{{ }}` replacement: view first, then outer page-template. Vars: `brand.name`, `brand.url`, `page.title`, `theme.appearance`, `version`, `cacheBust`. See [docs/templating.md](docs/templating.md).
113
+ - **Packaging** ([gulp/package.js](src/gulp/tasks/package.js)) — per-browser manifest normalization (JSON5 → strict JSON), zip, optional auto-publish.
114
+
115
+ See [docs/build-system.md](docs/build-system.md).
116
+
117
+ ### Build modes
118
+
119
+ - `BXM_BUILD_MODE=true` — production build (minified, no sourcemaps, dev-blocks stripped)
120
+ - `BXM_IS_PUBLISH=true` — also publish to Chrome / Firefox / Edge stores after packaging
121
+ - `BXM_TEST_MODE=true` — running inside BXM's test framework. Powers `Manager.isTesting()`.
122
+ - `BXM_LIVERELOAD_PORT=35729` — WebSocket port for `serve` task
123
+ - `BXM_LOG_FILE` — override the gulp stdout/stderr tee path, or `false` to disable it
124
+
125
+ ### Themes
126
+
127
+ Two themes ship with BXM: `bootstrap` (pure Bootstrap 5.3+) and `classy` (Bootstrap + custom design system). Plus `_template/` for new themes. Activate via `config.theme.id`; appearance via `config.theme.appearance` ('dark' / 'light'). Variables overridable from consumer SCSS via `@use 'browser-extension-manager' as * with ($primary: …)`. See [docs/themes.md](docs/themes.md).
128
+
129
+ ### Defaults system
130
+
131
+ `src/defaults/` is the starter template — copied to consumer projects on `npx bxm setup`. File behavior (overwrite/skip/template/rename) is controlled by `FILE_MAP` in [gulp/tasks/defaults.js](src/gulp/tasks/defaults.js). Most consumer files default to `overwrite: false` so user code is never clobbered. See [docs/defaults.md](docs/defaults.md).
132
+
133
+ ### Auto-translation
134
+
135
+ `npm run build` invokes the `translate` gulp task: reads `src/_locales/en/messages.json`, finds keys missing from the other 16 locales, fills them via Claude CLI. Existing translations are preserved. Languages: `zh es hi ar pt ru ja de fr ko ur id bn tl vi it`. See [docs/translations.md](docs/translations.md).
136
+
137
+ ### Build hooks
138
+
139
+ Two lifecycle hooks let consumers run custom logic during packaging:
140
+ - `hooks/build:pre.js` — after `dist/` is built but before `packaged/` is assembled
141
+ - `hooks/build:post.js` — after packaging (and after store publishing if `BXM_IS_PUBLISH=true`)
142
+
143
+ Both receive an `index` build-info object (package, manifest, config, paths, env). Async. See [docs/hooks.md](docs/hooks.md).
144
+
145
+ ### Cross-context helpers
146
+
147
+ Every Manager (build + 7 runtime contexts) has the same set of static + instance helpers via `attachTo(Manager)` mixin from `src/utils/mode-helpers.js`:
148
+
149
+ - `Manager.isDevelopment()` — running unpacked, and NOT testing
150
+ - `Manager.isTesting()` — `BXM_TEST_MODE=true` (takes precedence over development)
151
+ - `Manager.isProduction()` — running packed (from store), and NOT testing. A real positive check, NOT `!isDevelopment()`
152
+ - `Manager.getEnvironment()` — `'development' | 'testing' | 'production'` (mutually exclusive; testing wins)
153
+ - `Manager.getVersion()` — extension version (`chrome.runtime.getManifest().version` in browser, `package.json#version` in Node)
154
+
155
+ The three environment checks are mutually exclusive. Gate side effects on the INTENTIONAL check (`isProduction()` for prod-only, `isDevelopment() || isTesting()` for local-or-test) — never `!isDevelopment()`. Use these instead of grepping `process.env` ad-hoc. See [docs/environment-detection.md](docs/environment-detection.md).
156
+
157
+ ### Test framework
158
+
159
+ `npx bxm test` discovers + runs:
160
+ - `<BXM>/dist/test/suites/**/*.js` — framework defaults
161
+ - `<cwd>/test/**/*.js` — consumer suites
162
+
163
+ Four layers:
164
+ - **build** — plain Node, fast. Manager API, config validation, manifest shape, lib utilities.
165
+ - **background** — real MV3 service worker via Puppeteer + CDP. Boot sequence, `chrome.runtime` surface, storage round-trips, messaging.
166
+ - **view** — Chromium tab loading harness `popup.html` / `options.html` / `sidepanel.html`. DOM bindings, Manager surface, popup ↔ background messaging.
167
+ - **boot** — real headless Chromium loading the **consumer's** `packaged/<browser>/raw/` as an unpacked extension. End-to-end: does the real packaged extension boot?
168
+
169
+ Test files export `{ type, layer, description, tests, cleanup }` with `run` (build/background/view) or `inspect` (boot). Same `ctx.expect` / `state` / `skip` API as EM and BEM. CSP-safe ([docs/test-framework.md](docs/test-framework.md)) — test bodies are inlined as literal async-function expressions at runner build-time, not eval'd inside the SW.
170
+
171
+ **NEVER mock — test against the real harness.** Every layer gives you the real runtime (real MV3 SW, real Chromium tab + DOM, real packaged extension), so never hand-roll a `mockManager`, fake `chrome`/`browser`, or stubbed context. Only pure functions (zero I/O) are called directly. Real external APIs (Firebase, etc.) are GATED behind extended mode (`npx mgr test --extended` or `TEST_EXTENDED_MODE=true`) — normal mode skips them in-source via `ctx.skip(process.env.TEST_EXTENDED_MODE)`, NOT mocked; extended-mode tests must clean up anything they create externally. See [docs/test-framework.md](docs/test-framework.md).
172
+
173
+ See [docs/test-framework.md](docs/test-framework.md) and [docs/test-boot-layer.md](docs/test-boot-layer.md).
174
+
175
+ ### Test coverage
176
+
177
+ Every feature ships with tests at EVERY layer it has a surface in — logic (`build`/`background`), UI (`view` — real events on the real DOM), and end-to-end (`boot`). Skip a layer ONLY when the feature genuinely has no surface there (a pure build utility has no UI; a CSS-only tweak has no logic). "The logic test already covers it" is NOT a reason to skip the UI test — logic tests prove the logic, UI tests prove the wiring, boot tests prove the built artifact. See [docs/test-framework.md](docs/test-framework.md).
178
+
179
+ ## CLI
180
+
181
+ `npx bxm <command>` (aliases `xm`, `ext`, `mgr`, `browser-extension-manager`):
182
+
183
+ | Command | Description |
184
+ |---|---|
185
+ | `setup` | scaffold consumer, copy `src/defaults/`, ensure peer deps. Default when no command given. |
186
+ | `clean` | remove `dist/`, `packaged/`, `.cache/`, `.temp/` |
187
+ | `install` | install peer deps |
188
+ | `version` | print versions |
189
+ | `test` | run framework + project test suites |
190
+
191
+ See [docs/cli.md](docs/cli.md).
192
+
193
+ ## Dependency Resolution
194
+
195
+ - **Consumer code can `require()` any BXM dependency** — webpack's `resolve.modules` includes the framework's own `node_modules/`. Consumer projects do NOT need to `npm install firebase`, `web-manager`, or any other BXM transitive dep. If a dep doesn't resolve, the fix is in BXM's webpack config — not the consumer's `package.json`.
196
+ - **web-manager owns Firebase.** Consumer code NEVER imports Firebase directly (`require('firebase')` / `import('firebase/app')`). Use `import webManager from 'web-manager'` → `webManager.auth()`, `webManager.firestore()`. Same rule in EM and UJM.
197
+ - **`Manager.require(name)`** resolves from BXM's module context at runtime (static + prototype). Use in gulp tasks or unbundled code (e.g. test fixtures). Webpack `resolve.modules` handles the bundled case.
198
+
199
+ ## Development Workflow
200
+
201
+ - **🚫 NEVER run `npm start`** — it's the user's long-running dev watcher. Assume it's already running; if it isn't, **instruct the user to run it** rather than running it yourself (running it again kills theirs). To see output, **read the `logs/*.log` files** (`dev.log`, `build.log`, `test.log`) — never tail/attach to the process. Running `npx mgr test` is fine.
202
+ - **Where the output logs live:** the gulp pipeline tees all stdout/stderr to `<projectRoot>/logs/dev.log` (on `npm start`) or `logs/build.log` (on `npm run build`), truncated fresh each run, ANSI-stripped. `cat logs/dev.log` (or `grep` it) instead of scrolling scrollback. `npx mgr test` writes `logs/test.log`. See [docs/build-system.md](docs/build-system.md#log-files).
203
+ - **After editing files**, verify the gulp watcher recompiled successfully. Check for webpack/sass errors in the console output. A change that breaks the build is not a completed change.
204
+ - **Live-test UI changes via CDP.** After code changes compile, use the `chrome-devtools` MCP tools (screenshots, click, evaluate JS, console logs) to verify the change works in the running browser. This is the primary way to confirm UI changes — type-checking and test suites verify code correctness, not feature correctness. See `~/.claude/mcp-server/servers/chrome-devtools/CLAUDE.md`.
205
+
206
+ ## Supply-Chain Security
207
+
208
+ All `npm install` calls in CLI commands (`npx mgr i`, `npx mgr setup`) route through the `safeInstall()` helper (`src/lib/safe-install.js`). It prefixes `sfw` (Socket Firewall) when installed — blocking confirmed malware at the network level before packages reach disk. Falls back to plain npm if sfw isn't available. CI workflows install sfw globally and run `sfw npm install`. Installs will **fail if sfw detects confirmed malware** in any package in the dependency tree; non-critical CVEs and quality warnings pass through.
209
+
210
+ ## File Conventions
211
+
212
+ - **CommonJS** (`require()`) for build-time + Node code (gulp, CLI, tests). **ES modules** (`import`/`export default class`) for browser-context Manager files (`background.js`, `popup.js`, etc.) — they go through webpack/Babel.
213
+ - One `module.exports = ...` per file (CommonJS).
214
+ - Logical operators at the **start** of continuation lines.
215
+ - Short-circuit early returns rather than nested ifs.
216
+ - Prefer **`fs-jetpack`** over `fs-extra`.
217
+ - **No backwards compatibility** unless explicitly requested.
218
+ - **No paranoid `?.`** — see [the defensive-coding rule](https://anthropic.com/claude-code) (also enforced in `~/.claude/skills/js:patterns`). Framework internals deref directly; `?.` is for genuinely-uncertain values (user config sub-fields, `chrome.*` APIs that may be absent, regex matches, caught exceptions).
219
+ - **Browser-context modules are ES-module.** Webpack compiles them. Don't try to `require()` them from Node — they reference `window`, `document`, `chrome` at module-load time. Build-layer tests should target `lib/*.js` (Node-safe) or use BXM's public Manager API (`require('browser-extension-manager/build').getConfig()`).
220
+ - **Consumer pattern: use the public Manager API in tests.** Don't `require('json5')` or other transitive BXM deps directly from consumer test files — they're not in the consumer's `package.json` and resolution is fragile. Use `Manager.getConfig()` / `Manager.getManifest()` / `Manager.require('json5')`.
221
+
222
+ ## Doc-update parity
223
+
224
+ Whenever you make a behavioral change (new command, new flag, new pattern, removed feature), update:
225
+
226
+ 1. **`README.md`** — user-facing summary
227
+ 2. **`CLAUDE.md`** (this file) — architecture overview, one paragraph or cross-link
228
+ 3. **`docs/<topic>.md`** — the meat. If a topic doesn't have a doc yet, create one.
229
+ 4. **`CHANGELOG.md`** — if the project keeps one
230
+
231
+ Don't ship behavioral changes with stale docs. Validate first, then document — write docs that describe shipped reality, not intentions.
232
+
233
+ ## Documentation
234
+
235
+ API references for each subsystem live in `docs/`:
236
+
237
+ ### Architecture
238
+ - [docs/components.md](docs/components.md) — seven component contexts, three-part structure (view + styles + script), manifest wiring
239
+ - [docs/managers.md](docs/managers.md) — one-line bootstrap per context, import paths, `initialize()` flow
240
+ - [docs/environment-detection.md](docs/environment-detection.md) — `Manager.isTesting / isDevelopment / isProduction / getVersion`
241
+
242
+ ### Runtime
243
+ - [docs/extension.md](docs/extension.md) — cross-browser `chrome.*` / `browser.*` API wrapper
244
+ - [docs/auth.md](docs/auth.md) — cross-context auth sync, sign-in / load / sign-out flows, button CSS classes
245
+ - [docs/offscreen.md](docs/offscreen.md) — offscreen document lifecycle, creation from background, messaging
246
+ - [docs/xss-prevention.md](docs/xss-prevention.md) — escapeHTML/sanitizeURL canonical forms, extension attack vectors
247
+
248
+ ### Build
249
+ - [docs/build-system.md](docs/build-system.md) — gulp pipeline, webpack, sass, html, packaging
250
+ - [docs/templating.md](docs/templating.md) — `{{ }}` token replacement, available vars, page template
251
+ - [docs/css.md](docs/css.md) — SCSS load paths, framework + theme + project resolution
252
+ - [docs/themes.md](docs/themes.md) — bootstrap / classy / `_template`, variable overrides, dark mode
253
+ - [docs/icons.md](docs/icons.md) — one source icon → all generated sizes, manifest wiring
254
+ - [docs/defaults.md](docs/defaults.md) — `src/defaults/` system, `FILE_MAP` rules
255
+ - [docs/hooks.md](docs/hooks.md) — `build:pre` / `build:post` lifecycle hooks
256
+ - [docs/translations.md](docs/translations.md) — Claude CLI auto-translate to 16 languages
257
+
258
+ ### Operations
259
+ - [docs/cli.md](docs/cli.md) — commands, aliases, env var conventions
260
+ - [docs/cdp-debugging.md](docs/cdp-debugging.md) — launching a controllable Chrome (CDP), loading the unpacked extension (persistent agent profile — `--load-extension` is dead on stable Chrome), driving via MCP/CDP
261
+ - [docs/logging.md](docs/logging.md) — `dev.log` / `build.log` / `test.log` tee, controls
262
+ - [docs/common-mistakes.md](docs/common-mistakes.md) — the canonical "don't do this" list
263
+ - [docs/audit.md](docs/audit.md) — full-audit check catalog (U-xx universal / BXM-xx / F-xx IDs with severity + scope), protocol + fix loop
264
+ - [docs/publishing.md](docs/publishing.md) — Chrome / Firefox / Edge store auto-publishing, credentials, CI, store listing description format (`config/description.md`)
265
+
266
+ ### Testing
267
+ - [docs/test-framework.md](docs/test-framework.md) — writing tests, four layers, `ctx` + `expect` API
268
+ - [docs/test-boot-layer.md](docs/test-boot-layer.md) — boot layer (loads consumer's actual packaged extension)
package/README.md CHANGED
@@ -167,6 +167,10 @@ Add these CSS classes to HTML elements for declarative auth UI:
167
167
 
168
168
  Full guide: [docs/auth.md](docs/auth.md).
169
169
 
170
+ ## 🔒 Supply-chain security
171
+
172
+ All `npm install` calls in BXM CLI commands (`npx bxm setup`, `npx bxm install`) route through [Socket Firewall](https://socket.dev/) when installed — blocking confirmed malware at the network level before packages reach disk. Falls back to plain npm if sfw isn't available. Consumer CI workflows (`publish.yml` default) install sfw globally and run `sfw npm install`.
173
+
170
174
  ## 📚 Documentation
171
175
 
172
176
  In-depth docs for every subsystem live in [docs/](docs/). See [CLAUDE.md](CLAUDE.md) for the architecture overview + table of contents.
@@ -1,7 +1,7 @@
1
1
  // Libraries
2
2
  const Manager = new (require('../build.js'));
3
3
  const logger = Manager.logger('install');
4
- const { execute } = require('node-powertools');
4
+ const { safeInstall } = require('../lib/safe-install');
5
5
  const os = require('os');
6
6
 
7
7
  // Load package
@@ -48,5 +48,5 @@ module.exports = async function (options) {
48
48
  };
49
49
 
50
50
  function install(command) {
51
- return execute(command, { log: true });
51
+ return safeInstall(command);
52
52
  }
@@ -6,6 +6,7 @@ const path = require('path');
6
6
  const jetpack = require('fs-jetpack');
7
7
  const version = require('wonderful-version');
8
8
  const { execute, template, force } = require('node-powertools');
9
+ const { safeInstall } = require('../lib/safe-install');
9
10
  const NPM = require('npm-api');
10
11
  const glob = require('glob').globSync;
11
12
  const { minimatch } = require('minimatch');
@@ -212,7 +213,7 @@ function install(package, ver, location) {
212
213
  logger.log('Installing:', command);
213
214
 
214
215
  // Execute
215
- return execute(command, { log: true })
216
+ return safeInstall(command)
216
217
  .then(async () => {
217
218
  // Read new project
218
219
  const projectUpdated = jetpack.read(path.join(process.cwd(), 'package.json'), 'json');
@@ -52,8 +52,11 @@ jobs:
52
52
  with:
53
53
  node-version: ${{ env.NODE_VERSION }}
54
54
 
55
+ - name: Install Socket Firewall
56
+ run: npm install -g sfw
57
+
55
58
  - name: Install dependencies
56
- run: npm install
59
+ run: sfw npm install
57
60
 
58
61
  - name: Log dependency versions
59
62
  run: |
@@ -0,0 +1,13 @@
1
+ const { execute } = require('node-powertools');
2
+
3
+ let _hasSfw;
4
+ async function safeInstall(command, options) {
5
+ if (_hasSfw === undefined) {
6
+ _hasSfw = await execute('sfw --version', { log: false }).then(() => true).catch(() => false);
7
+ }
8
+ const isInstall = /^npm\s+(install|i)\b/.test(command);
9
+ const prefix = (_hasSfw && isInstall) ? 'sfw ' : '';
10
+ return execute(`${prefix}${command}`, options || { log: true });
11
+ }
12
+
13
+ module.exports = { safeInstall };
package/docs/audit.md ADDED
@@ -0,0 +1,67 @@
1
+ # Audit Workflow
2
+
3
+ Full-project audit for BXM — runs against a CONSUMER extension or the FRAMEWORK repo itself (scope auto-detected). Invoked via the `omega:bxm` skill (`/omega:bxm audit`) or any "audit this extension/project" request.
4
+
5
+ Every check has a stable ID, a severity, and a scope. Findings are reported as `ID @ file:line`, fixed one at a time, then re-verified. The tables below do NOT restate the rules — each check links to the doc that owns the rule and the fix.
6
+
7
+ ## Protocol
8
+
9
+ 1. **Detect scope** — read `package.json`: `name` is `browser-extension-manager` → **framework audit** (U + BXM + F checks); `browser-extension-manager` in (dev)dependencies → **consumer audit** (U + BXM checks).
10
+ 2. **Run the catalog** — every check matching the scope. Search with Grep/Glob/Read over `src/` (+ `test/`, `config/`, `hooks/`); ALWAYS exclude `dist/`, `packaged/`, `node_modules/`, `_legacy/`, `_backup/`, `.temp/`, `.cache/`. Record each finding as `ID @ file:line` + a one-line description.
11
+ 3. **Persist the report** — write the findings list to `.temp/audit/claude-audit.md` so a long fix loop survives session breaks. Summarize counts by severity in chat.
12
+ 4. **Fix loop** — TodoWrite per finding, highest severity first, ONE at a time: mark in-progress → root cause → fix → verify → complete. Ask before structural or destructive fixes (file deletions, component restructures, manifest permission changes).
13
+ 5. **Re-verify** — re-run every check that produced findings until clean; finish with `npx mgr test` (must be green).
14
+ 6. **Doc parity** — if fixes changed behavior, update README / CLAUDE.md / `docs/<topic>.md` / CHANGELOG in the same change set.
15
+
16
+ Severity: **CRIT** security or broken functionality · **HIGH** hard-rule violation · **MED** convention drift · **LOW** optional improvement.
17
+ Scope: **C** consumer · **F** framework repo · **B** both.
18
+
19
+ ## Universal checks (U-xx)
20
+
21
+ Mirrored across all four OMEGA frameworks (UJM / BEM / BXM / EM) — same ID means the same check everywhere.
22
+
23
+ | ID | Sev | Scope | Check |
24
+ |----|-----|-------|-------|
25
+ | U-01 | HIGH | B | Every feature has tests at EVERY layer it surfaces (build / background / view / boot) — never mocked, real harness only ([test-framework.md](test-framework.md)) |
26
+ | U-02 | HIGH | B | Test hygiene — real-external-API tests gated behind `TEST_EXTENDED_MODE` in-source via `ctx.skip(...)` (not mocked); no tests that assert nothing ([test-framework.md](test-framework.md)) |
27
+ | U-03 | CRIT | B | XSS — inline `webManager.utilities().escapeHTML(value)` at EVERY DOM sink, `sanitizeURL(url)` at executable URL sinks, zero local escape helpers ([xss-prevention.md](xss-prevention.md)) |
28
+ | U-04 | HIGH | B | web-manager owns Firebase — no direct `firebase` imports anywhere; `webManager.auth()` / `.firestore()` ([common-mistakes.md](common-mistakes.md)) |
29
+ | U-05 | HIGH | C | No BXM transitive deps installed in the consumer `package.json` (`firebase`, `web-manager`, `json5`, …) — webpack `resolve.modules` / `Manager.require()` resolve them ([common-mistakes.md](common-mistakes.md), [CLAUDE.md](../CLAUDE.md) §Dependency Resolution) |
30
+ | U-06 | HIGH | B | Env behavior gated on the INTENTIONAL check — `isProduction()` or `isDevelopment() \|\| isTesting()`, never `!isDevelopment()`; no ad-hoc `process.env.BXM_*` reads where a helper exists ([environment-detection.md](environment-detection.md)) |
31
+ | U-07 | HIGH | B | Config canon — `config/browser-extension-manager.json` + `src/manifest.json` match the documented shapes; canonical cross-framework blocks (`brand`, flat 8-key `firebaseConfig`, …) not reinvented ([defaults.md](defaults.md), [components.md](components.md)) |
32
+ | U-08 | CRIT | B | No private credentials committed — store-publishing credentials, `.env`, tokens, secret keys; `.gitignore` covers them ([publishing.md](publishing.md)). (The Firebase WEB `apiKey` is public by design — do NOT flag it.) |
33
+ | U-09 | HIGH | B | Source discipline — nothing edited in `dist/` or `packaged/` (including `dist/manifest.json`); no live code referencing `_legacy/` / `_backup/` ([build-system.md](build-system.md)) |
34
+ | U-10 | MED | B | Doc parity — README / CLAUDE.md / `docs/` / CHANGELOG match shipped behavior; CLAUDE.md < 250 lines; the docs index lists every `docs/*.md`; no stale names for renamed commands/patterns |
35
+ | U-11 | MED | B | SSOT/DRY — no duplicated constants/config/logic; one authoritative home per value, imported everywhere else |
36
+ | U-12 | MED | B | JS conventions — file structure, JSDoc, short-circuit returns, leading logical operators, `fs-jetpack`, one `module.exports` per file (global `js:patterns` skill + [CLAUDE.md](../CLAUDE.md) §File Conventions) |
37
+ | U-13 | MED | B | Dead code & stale patterns — no orphaned `src/` files nothing imports; no unused components/views; inventory TODO/FIXME (report only) |
38
+ | U-14 | LOW | B | Dependency health — review `npm outdated` / `npm audit`; apply fixes via the `general:update-packages` workflow (includes supply-chain checks) |
39
+
40
+ ## BXM-specific checks
41
+
42
+ | ID | Sev | Scope | Check |
43
+ |----|-----|-------|-------|
44
+ | BXM-01 | HIGH | B | Cross-browser wrapper — `extension.*` everywhere; never raw `chrome.*` / `browser.*` in component code ([extension.md](extension.md)) |
45
+ | BXM-02 | HIGH | B | Component structure — views are HTML FRAGMENTS (not full documents); three parts at conventional paths (view + styles + script) ([components.md](components.md)) |
46
+ | BXM-03 | HIGH | B | Service-worker-safe background — no DOM/`window`/`document` APIs in `background`; DOM-needing work goes to an offscreen document ([components.md](components.md), [offscreen.md](offscreen.md), [common-mistakes.md](common-mistakes.md)) |
47
+ | BXM-04 | MED | C | Permissions justified — every manifest permission maps to a feature actually used (`offscreen` ↔ offscreen component, `tabs` ↔ auth sync, …); remove unused ones ([components.md](components.md), [auth.md](auth.md), [offscreen.md](offscreen.md)) |
48
+ | BXM-05 | MED | C | Store listing — `config/description.md` follows the listing format (Bonus/Privacy sections intact); stale translation caches cleared after description changes ([publishing.md](publishing.md), [translations.md](translations.md)) |
49
+ | BXM-06 | MED | B | Accessibility basics in popup/options/sidepanel/pages views — meaningful `alt` text, labeled form fields, real `<button>`/`<a>` elements (no clickable `div`s) |
50
+
51
+ ## Framework-repo checks (F-xx)
52
+
53
+ Only when auditing the BXM repo itself. Mirrored across the four frameworks.
54
+
55
+ | ID | Sev | Check |
56
+ |----|-----|-------|
57
+ | F-01 | MED | Sister parity — mirrored sections (config shapes, test contract, CLAUDE.md skeleton, shared env/test conventions) in sync with UJM / BEM / EM; deviations are deliberate and documented |
58
+ | F-02 | HIGH | Consumer-shipped defaults in sync — what `npx bxm setup` scaffolds (`src/defaults/` via `FILE_MAP`) matches current conventions and docs ([defaults.md](defaults.md)) |
59
+ | F-03 | MED | Docs completeness — every `docs/*.md` indexed in CLAUDE.md; every subsystem has a doc; no "(planned)" links for things that have shipped |
60
+ | F-04 | HIGH | `npx mgr test mgr:` green before treating the audit as complete |
61
+
62
+ ## See also
63
+
64
+ - [xss-prevention.md](xss-prevention.md) — the escaping rules behind U-03
65
+ - [extension.md](extension.md) — the wrapper rule behind BXM-01
66
+ - [components.md](components.md) — the structure rules behind BXM-02 / BXM-03 / BXM-04
67
+ - [test-framework.md](test-framework.md) — the layers behind U-01 / U-02
package/docs/auth.md ADDED
@@ -0,0 +1,147 @@
1
+ # Cross-Context Auth
2
+
3
+ Browser extensions have multiple isolated JavaScript contexts (background SW, popup, options, sidepanel, pages) — each runs its own Firebase instance. BXM syncs them via messaging so a sign-in in one context is reflected in all of them without using `chrome.storage`.
4
+
5
+ ## The core idea
6
+
7
+ **Background.js is the source of truth.** Other contexts compare their auth state with background's on load, and sync up if different. Sign-in/sign-out events are broadcast from background to everyone.
8
+
9
+ This pattern avoids `chrome.storage` (no cross-context tokens on disk, no race conditions). Firebase persists session state in IndexedDB per-context.
10
+
11
+ ## Sign-in flow
12
+
13
+ ```
14
+ User clicks .auth-signin-btn (in popup/options/sidepanel/page)
15
+
16
+ openAuthPage() opens https://<authDomain>/token?authSourceTabId=<n>
17
+
18
+ Website authenticates, redirects to /token?authToken=xxx
19
+
20
+ background.js's tabs.onUpdated listener detects authDomain URL + authToken param
21
+
22
+ background.js calls signInWithCustomToken(authToken)
23
+
24
+ background.js broadcasts the token to all open contexts via chrome.runtime.sendMessage
25
+
26
+ background.js closes the /token tab, reactivates original tab (using authSourceTabId)
27
+
28
+ Open contexts receive the broadcast, signInWithCustomToken() locally
29
+ ```
30
+
31
+ ## Context-load flow
32
+
33
+ When a popup/options/sidepanel/page boots:
34
+
35
+ ```
36
+ Context loads
37
+
38
+ Web Manager initializes, waits for auth to settle (auth.listen({ once: true }))
39
+
40
+ Sends `bxm:syncAuth` message to background, including local UID
41
+
42
+ Background compares UIDs:
43
+ - Same UID (including both null) → in sync, no action
44
+ - Different UID, background signed in → background fetches fresh custom token from server,
45
+ sends to context, context signs in
46
+ - Background signed out, context signed in → tells context to sign out
47
+ ```
48
+
49
+ ## Sign-out flow
50
+
51
+ ```
52
+ User clicks .auth-signout-btn
53
+
54
+ Web Manager signs out that context's Firebase
55
+
56
+ setupSignOutListener() detects sign-out, sends `bxm:signOut` to background
57
+
58
+ background.js signs out its Firebase
59
+
60
+ background.js broadcasts `bxm:signOut` to all other contexts
61
+
62
+ All contexts sign out
63
+ ```
64
+
65
+ ## Required setup
66
+
67
+ 1. **Add `authDomain`** to your Firebase config in `config/browser-extension-manager.json`:
68
+ ```jsonc
69
+ {
70
+ firebaseConfig: {
71
+ apiKey: '...',
72
+ authDomain: 'tabblar.com', // ← required for /token redirect flow
73
+ projectId: 'tabblar',
74
+ // ...
75
+ }
76
+ }
77
+ ```
78
+ 2. **Add `tabs` permission** to `src/manifest.json` — needed for `chrome.tabs.onUpdated` listener that detects the `/token` redirect.
79
+
80
+ ## Functions in `lib/auth-helpers.js`
81
+
82
+ [src/lib/auth-helpers.js](../src/lib/auth-helpers.js):
83
+
84
+ | Function | Purpose |
85
+ |---|---|
86
+ | `syncWithBackground(context)` | Called on context boot. Compares context's UID with background's, syncs if different. |
87
+ | `setupAuthBroadcastListener(context)` | Listens for sign-in / sign-out broadcasts from background. |
88
+ | `setupSignOutListener(context)` | Notifies background when this context signs out. |
89
+ | `setupAuthEventListeners(context)` | Wires delegated click handlers for `.auth-signin-btn` / `.auth-account-btn`. |
90
+ | `openAuthPage(context, options)` | Opens the website's `/token` page with `authSourceTabId` for tab restoration. |
91
+
92
+ Every popup/options/sidepanel/page Manager calls these automatically in `initialize()`. See [managers.md](managers.md).
93
+
94
+ ## Auth button classes
95
+
96
+ Add these classes to HTML elements to wire up auth UI without writing JS:
97
+
98
+ | Class | Action |
99
+ |---|---|
100
+ | `.auth-signin-btn` | Opens `/token` page on website. After authentication, signs in across all contexts. |
101
+ | `.auth-signout-btn` | Signs out via Web Manager. Notifies background, which broadcasts to other contexts. |
102
+ | `.auth-account-btn` | Opens `/account` page on website (for billing, profile, etc.) |
103
+
104
+ ## Reactive bindings
105
+
106
+ Web Manager exposes `data-wm-bind` attributes for show/hide/text/attr based on auth state:
107
+
108
+ ```html
109
+ <!-- Sign-in button shown when logged out -->
110
+ <button class="btn auth-signin-btn" data-wm-bind="@show !auth.user">
111
+ Sign In
112
+ </button>
113
+
114
+ <!-- Account UI shown when logged in -->
115
+ <div data-wm-bind="@show auth.user" hidden>
116
+ <img data-wm-bind="@attr src auth.user.photoURL">
117
+ <span data-wm-bind="@text auth.user.displayName"></span>
118
+ <a class="auth-account-btn" href="#">Account</a>
119
+ <button class="auth-signout-btn">Sign Out</button>
120
+ </div>
121
+ ```
122
+
123
+ | Binding | Behavior |
124
+ |---|---|
125
+ | `@show auth.user` | Element visible only when signed in |
126
+ | `@show !auth.user` | Element visible only when signed out |
127
+ | `@text auth.user.displayName` | Element text content set from path |
128
+ | `@text auth.user.email` | Same — any path under `auth.user.*` |
129
+ | `@attr src auth.user.photoURL` | Set element attribute from path |
130
+
131
+ These bindings live in Web Manager, not BXM — but they're how every BXM extension surfaces auth state in views.
132
+
133
+ ## Important implementation details
134
+
135
+ 1. **No storage.** Auth state is NOT in `chrome.storage`. Firebase persists sessions in IndexedDB per-context. Web Manager handles UI bindings off those persisted sessions.
136
+
137
+ 2. **Firebase in service workers requires static imports.** Dynamic `import()` fails with webpack chunking inside SWs. BXM's background.js uses static `import { initializeApp } from 'firebase/app'`.
138
+
139
+ 3. **Config path is fixed.** `authDomain` lives at `config.firebaseConfig.authDomain` (loaded via the `BXM_BUILD_JSON` webpack DefinePlugin replacement).
140
+
141
+ 4. **Tabs permission required.** Without it, background.js can't watch for `/token?authToken=…` redirects.
142
+
143
+ ## See also
144
+
145
+ - [managers.md](managers.md) — each Manager's `initialize()` wires the auth helpers
146
+ - [components.md](components.md) — which contexts participate in auth sync
147
+ - [extension.md](extension.md) — `chrome.tabs.onUpdated` access via the extension wrapper