web-manager 4.1.42 → 4.2.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/CHANGELOG.md CHANGED
@@ -14,6 +14,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
14
14
  - `Fixed` for any bug fixes.
15
15
  - `Security` in case of vulnerabilities.
16
16
 
17
+ ---
18
+ ## [4.2.0] - 2026-05-21
19
+ ### Added
20
+ - **Consent defaults in `DEFAULT_ACCOUNT`.** Phase A companion to `backend-manager` v5.2.0's marketing-consent system. `src/modules/auth.js` now defaults `account.consent.{legal,marketing}` to the canonical shape (`status: 'revoked'`, `grantedAt`/`revokedAt` with null leaves) so `resolveAccount()` always populates the field for legacy users whose Firestore doc predates the consent system. Without this, the account-page email-preferences toggle would read `undefined` for pre-migration users and mis-state.
21
+ - **`docs/<topic>.md` deep references.** New `docs/architecture.md`, `docs/build-system.md`, `docs/code-patterns.md`, `docs/common-tasks.md`, `docs/dependencies.md`, `docs/modules.md`, `docs/testing.md`. Mirrors the architectural-overview-plus-deep-references pattern used in `backend-manager` and `electron-manager`.
22
+
23
+ ### Changed
24
+ - **`CLAUDE.md` refactored to the architectural-overview pattern.** Trimmed from 301 lines to ~80; per-subsystem detail moved to the new `docs/*.md` files. Keeps Claude's loaded-context cost down on every conversation.
25
+
26
+ ### Fixed
27
+ - **Toast notification width.** `src/modules/utilities.js`: notifications now have `width: calc(100% - 2rem); max-width: 500px` so longer text doesn't render as a narrow cramped strip at the top of the page.
28
+
17
29
  ---
18
30
  ## [4.1.42] - 2026-05-11
19
31
  ### Changed
package/CLAUDE.md CHANGED
@@ -1,301 +1,82 @@
1
- # Web Manager - AI Agent Guide
1
+ # Web Manager
2
2
 
3
- This document helps AI agents understand the web-manager codebase for effective contributions.
3
+ > **Note for contributors and Claude:** This file is the architectural overview — identity, top-level conventions, and a map to deep references. The **meat** (module APIs, patterns, behavior tables) 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
4
 
5
- ## Project Overview
5
+ ## Identity
6
6
 
7
- **Purpose**: Modern JavaScript utility library for web applications with Firebase integration. Provides authentication, data binding, storage, push notifications, error tracking, and more.
7
+ Web Manager is a modern JavaScript utility library for web applications with Firebase integration. It runs in the browser, in Electron's renderer process, and inside browser extensions (content scripts, popups, background pages). Provides:
8
8
 
9
- **Target Environments**:
10
- - Web (primary, webpack-optimized)
11
- - Electron (renderer process)
12
- - Chrome/Firefox Extensions (content scripts, popups)
9
+ - A singleton `Manager` instance exposing authentication, reactive DOM data binding, Firestore, storage, push notifications, error tracking (Sentry), service-worker helpers, and DOM/utility functions
10
+ - Lazy Firebase imports to keep consumer bundles small
11
+ - Reactive `data-wm-bind` DOM directives wired to auth + usage state
12
+ - A `resolveSubscription()` helper unified with backend-manager's `User.resolveSubscription()` so subscription-state logic is identical across frontend and backend
13
13
 
14
- **Version**: 4.0.x | **Node**: >=12 | **License**: CC-BY-4.0
14
+ ### Consumed by the frontend Manager family
15
15
 
16
- ## Architecture
17
-
18
- ### Singleton Pattern
19
- The library exports a singleton `Manager` instance. Import it directly from any file — it's always the same initialized instance:
20
- ```javascript
21
- import webManager from 'web-manager';
22
-
23
- // Same instance everywhere — config, auth, firestore, all ready
24
- webManager.auth().listen((state) => { ... });
25
- webManager.utilities().escapeHTML(untrustedText);
26
- webManager.config.environment; // 'development' or 'production'
27
- ```
28
- **Do NOT create new instances** (`new Manager()`). UJM and BXM initialize the singleton — every import gets that same object. Do NOT pass `webManager` through function params or store it in module-level variables — just import it.
29
-
30
- ### Directory Structure
31
- ```
32
- web-manager/
33
- ├── src/ # Source code (ES6+)
34
- │ ├── index.js # Manager class, initialization, Firebase setup
35
- │ └── modules/ # Feature modules
36
- │ ├── auth.js # Firebase Auth wrapper
37
- │ ├── bindings.js # Reactive DOM data binding
38
- │ ├── dom.js # loadScript, ready utilities
39
- │ ├── firestore.js # Firestore wrapper with chainable queries
40
- │ ├── notifications.js # FCM push notifications
41
- │ ├── sentry.js # Error tracking integration
42
- │ ├── service-worker.js # SW registration and messaging
43
- │ ├── storage.js # localStorage/sessionStorage wrapper
44
- │ └── utilities.js # Helper functions (clipboard, escape, etc.)
45
- ├── dist/ # Transpiled ES5 output (generated)
46
- ├── _legacy/ # Old implementation (reference only, DO NOT MODIFY)
47
- └── test/ # Mocha tests
48
- ```
49
-
50
- ### Module Dependencies
51
- ```
52
- Manager (index.js)
53
- ├── Storage (standalone, no deps)
54
- ├── Auth → Manager, Bindings, Storage, Firestore
55
- ├── Bindings → Manager
56
- ├── Firestore → Manager (lazy Firebase import)
57
- ├── Notifications → Manager, Storage, Firestore
58
- ├── ServiceWorker → Manager
59
- ├── Sentry → Manager (dynamic import)
60
- ├── DOM utilities (standalone)
61
- └── Utilities (standalone)
62
- ```
63
-
64
- ## Key Patterns
16
+ Web Manager is the runtime singleton powering **Ultimate Jekyll Manager (UJM)**, **Browser Extension Manager (BXM)**, and **Electron Manager (EM)**. Each framework initializes the singleton once and exposes it as `manager.webManager`. Any consumer of those frameworks gets a fully-wired web-manager via `import webManager from 'web-manager'`.
65
17
 
66
- ### 1. Early Return (Short-Circuit)
67
- Always use early returns instead of nested conditionals:
68
- ```javascript
69
- // CORRECT
70
- function doSomething() {
71
- if (!condition) {
72
- return;
73
- }
74
- // Long code block...
75
- }
76
-
77
- // WRONG
78
- function doSomething() {
79
- if (condition) {
80
- // Long code block...
81
- }
82
- }
83
- ```
84
-
85
- ### 2. DOM Element Naming
86
- Prefix DOM element variables with `$`:
87
- ```javascript
88
- const $button = document.querySelector('.submit-btn');
89
- const $input = document.getElementById('email');
90
- ```
91
-
92
- ### 3. Logical Operator Formatting
93
- Place operators at the START of continuation lines:
94
- ```javascript
95
- // CORRECT
96
- const result = conditionA
97
- || conditionB
98
- || conditionC;
18
+ ## Recommended skills
99
19
 
100
- // WRONG
101
- const result = conditionA ||
102
- conditionB ||
103
- conditionC;
104
- ```
20
+ - **`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.
105
21
 
106
- ### 4. Firestore Path Syntax
107
- Prefer path syntax over collection/doc chaining:
108
- ```javascript
109
- // PREFERRED
110
- db.doc('users/userId')
22
+ ## Quick Start
111
23
 
112
- // ALSO SUPPORTED
113
- db.doc('users', 'userId')
114
- ```
24
+ ### For Consuming Projects
115
25
 
116
- ### 5. Dynamic Imports
117
- Firebase modules are dynamically imported to reduce bundle size:
118
- ```javascript
119
- const { initializeApp } = await import('firebase/app');
120
- const { getAuth } = await import('firebase/auth');
121
- ```
26
+ Web Manager is consumed indirectly through UJM, BXM, or EM — those frameworks initialize the singleton for you. Inside any consuming code:
122
27
 
123
- ### 6. Configuration Deep Merge
124
- User config is deep-merged with defaults in `_processConfiguration()`. Only override what you need:
125
28
  ```javascript
126
- // Defaults defined in _processConfiguration()
127
- const defaults = {
128
- environment: 'production',
129
- firebase: { app: { enabled: true, config: {} } },
130
- // ...
131
- };
132
- ```
133
-
134
- ### 7. Event Delegation
135
- Auth UI uses event delegation on document body:
136
- ```javascript
137
- document.body.addEventListener('click', (e) => {
138
- if (e.target.closest('.auth-signout-btn')) {
139
- // Handle signout
140
- }
141
- });
142
- ```
143
-
144
- ## Module Quick Reference
145
-
146
- ### Storage (`storage.js`)
147
- - **Class**: `Storage`
148
- - **Key Methods**: `get(path, default)`, `set(path, value)`, `remove(path)`, `clear()`
149
- - **Session**: Same methods under `.session` namespace
150
- - **Storage Key**: `_manager` in localStorage
151
-
152
- ### Auth (`auth.js`)
153
- - **Class**: `Auth`
154
- - **Key Methods**: `listen(options, callback)`, `isAuthenticated()`, `getUser()`, `signInWithEmailAndPassword()`, `signOut()`, `getIdToken()`, `resolveSubscription(account?)`
155
- - **Bindings**: Updates `auth` and `usage` context on auth settle
156
- - **Usage Resolution**: `_resolveUsage(state)` merges `account.usage` (Firestore) with product limits from `config.payment.products` (OMEGA-canonical shape — same key name in BEM, UJM, and EM) to produce the `usage` bindings key (e.g., `{ credits: { monthly: 5, limit: 100 } }`)
157
-
158
- #### resolveSubscription(account?)
159
- Derives calculated subscription fields from raw account data. Returns only fields that require derivation logic — raw data (product.id, status, trial, cancellation) lives on `account.subscription` directly.
160
-
161
- ```javascript
162
- const resolved = auth.resolveSubscription(account);
163
- // Returns: { plan, active, trialing, cancelling }
164
- ```
165
- - `plan`: Effective plan ID the user has access to RIGHT NOW (`'basic'` if cancelled/suspended)
166
- - `active`: User has active access (active, trialing, or cancelling — all mean the user can use the product)
167
- - `trialing`: In an active trial (status `'active'` + `trial.claimed` + unexpired `trial.expires`)
168
- - `cancelling`: Cancellation pending (status `'active'` + `cancellation.pending` + NOT trialing)
169
-
170
- **Unified with BEM**: The same function exists on `User.resolveSubscription(account)` in backend-manager (`helpers/user.js`) with identical logic and return shape.
171
-
172
- #### Auth Settler Pattern
173
- Auth uses a promise-based settler (`_authReady`) that resolves once Firebase's first `onAuthStateChanged` fires — the moment auth state is guaranteed (authenticated user OR null). This eliminates race conditions.
174
-
175
- - **`once` listeners** (`listen({ once: true }, cb)`): Wait for `_authReady`, fire once, done. No cleanup needed.
176
- - **Persistent listeners** (`listen({}, cb)`): Subscribe to `_authStateCallbacks`. If auth already settled when registered, catch up via `_authReady.then()`. Otherwise, `_handleAuthStateChange` handles the initial call naturally.
177
- - **`_hasProcessedStateChange`**: Ensures bindings/storage updates run only once per auth state change across all listeners.
178
- - **Manager owns the promise**: `_authReady` and `_authReadyResolve` live on the Manager instance. The `onAuthStateChanged` callback in `index.js` resolves it on first fire and sets `_firebaseAuthInitialized = true`.
179
-
180
- ### Bindings (`bindings.js`)
181
- - **Class**: `Bindings`
182
- - **Key Methods**: `update(data)`, `getContext()`, `clear()`
183
- - **HTML Attr**: `data-wm-bind`
184
- - **Actions**: `@text`, `@value`, `@show`, `@hide`, `@attr`, `@style`
185
-
186
- ### Firestore (`firestore.js`)
187
- - **Class**: `Firestore`
188
- - **Key Methods**: `doc(path)`, `collection(path)`
189
- - **Doc Methods**: `.get()`, `.set()`, `.update()`, `.delete()`
190
- - **Query Methods**: `.where()`, `.orderBy()`, `.limit()`, `.startAt()`, `.endAt()`
191
-
192
- ### Notifications (`notifications.js`)
193
- - **Class**: `Notifications`
194
- - **Key Methods**: `isSupported()`, `isSubscribed()`, `subscribe()`, `unsubscribe()`, `getToken()`, `onMessage()`
195
- - **Storage**: Saves to localStorage and Firestore
196
-
197
- ### ServiceWorker (`service-worker.js`)
198
- - **Class**: `ServiceWorker`
199
- - **Key Methods**: `isSupported()`, `register()`, `ready()`, `postMessage()`, `onMessage()`, `getState()`
200
-
201
- ### Sentry (`sentry.js`)
202
- - **Class**: `Sentry` (named `mod` internally)
203
- - **Key Methods**: `init(config)`, `captureException(error, context)`
204
- - **Filtering**: Blocks dev mode, Lighthouse, Selenium/Puppeteer
205
-
206
- ### DOM (`dom.js`)
207
- - **Exports**: `loadScript(options)`, `ready()`
208
- - **loadScript Options**: src, async, defer, crossorigin, integrity, timeout, retries
209
-
210
- ### Utilities (`utilities.js`)
211
- - **Exports**: `clipboardCopy()`, `escapeHTML()`, `showNotification()`, `getPlatform()`, `getBrowser()`, `getRuntime()`, `isMobile()`, `getDevice()`, `getContext()`
212
-
213
- ## Build System
214
-
215
- ### prepare-package
216
- The library uses `prepare-package` for ES5 transpilation:
29
+ import webManager from 'web-manager';
217
30
 
218
- ```json
219
- {
220
- "preparePackage": {
221
- "input": "./src",
222
- "output": "./dist"
223
- }
224
- }
31
+ webManager.auth().listen({ once: true }, async () => { /* auth settled */ });
32
+ webManager.utilities().escapeHTML(untrustedText);
33
+ webManager.firestore().doc('users/abc').get();
225
34
  ```
226
35
 
227
- **Commands**:
228
- - `npm run prepare` - Build once
229
- - `npm start` - Watch mode
230
- - `npm test` - Run Mocha tests
36
+ ### For Framework Development (This Repository)
231
37
 
232
- ### Package Exports
233
- ```json
234
- {
235
- "main": "dist/index.js",
236
- "module": "src/index.js",
237
- "exports": {
238
- ".": "./dist/index.js",
239
- "./modules/*": "./dist/modules/*"
240
- }
241
- }
242
- ```
38
+ 1. `npm install` — install Web Manager's own deps
39
+ 2. `npm run prepare` — build once: copies `src/` → `dist/` via prepare-package (ES5 transpile)
40
+ 3. `npm start` — watch mode (rebuild on change)
41
+ 4. `npm test` — run Mocha tests
243
42
 
244
- ## Testing
43
+ > **Important:** Web Manager is a library, not an app. There is no `npm run build` / `npm run serve` here. Consume it from inside a UJM / BXM / EM project for end-to-end behavior.
245
44
 
246
- Tests are in `test/test.js` using Mocha:
247
- ```bash
248
- npm test
249
- ```
45
+ ## Architecture
250
46
 
251
- Current test coverage is minimal - focuses on configuration and storage.
47
+ Web Manager exports a singleton `Manager` instance from `src/index.js`. Every `import webManager from 'web-manager'` returns the same already-initialized object — do NOT call `new Manager()`, and do NOT pass `webManager` through function params or module-level variables.
252
48
 
253
- ## Common Tasks
49
+ The singleton owns nine feature modules under `src/modules/`: `storage`, `auth`, `bindings`, `firestore`, `notifications`, `service-worker`, `sentry`, `dom`, `utilities`. Firebase modules are dynamically imported to keep the bundle small. See [docs/architecture.md](docs/architecture.md) for the directory structure and module dependency graph, and [docs/modules.md](docs/modules.md) for the API reference of each module.
254
50
 
255
- ### Adding a New Utility Function
256
- 1. Add function to `src/modules/utilities.js`
257
- 2. Export it: `export function myFunction() { ... }`
258
- 3. Update README.md with documentation
259
- 4. Run `npm run prepare` to build
51
+ ## File Conventions
260
52
 
261
- ### Adding a New Module
262
- 1. Create `src/modules/my-module.js`
263
- 2. Export class: `export default class MyModule { constructor(manager) { ... } }`
264
- 3. Import in `src/index.js`: `import MyModule from './modules/my-module.js'`
265
- 4. Add to Manager constructor: `this._myModule = new MyModule(this)`
266
- 5. Add getter: `myModule() { return this._myModule; }`
267
- 6. Update README.md
268
- 7. Run `npm run prepare`
53
+ - **CommonJS-friendly ES6+** in `src/`. `prepare-package` transpiles to ES5 in `dist/`.
54
+ - **`fs-jetpack`** over `fs` / `fs-extra` for any file operations in tests/scripts.
55
+ - **No TypeScript** pure JavaScript library.
56
+ - **Template strings** use backticks for string interpolation.
57
+ - **DO NOT modify `_legacy/`** reference only, frozen for historical context.
58
+ - **No backwards compatibility** unless explicitly requested — just change to the new way.
59
+ - **Early-return / short-circuit** style throughout — see [docs/code-patterns.md](docs/code-patterns.md) for the full code-pattern checklist (`$`-prefixed DOM vars, operators at start of continuation lines, Firestore path syntax, dynamic imports, config deep-merge, event delegation).
269
60
 
270
- ### Modifying Configuration Defaults
271
- 1. Edit `_processConfiguration()` in `src/index.js`
272
- 2. Add to `defaults` object (e.g., `payment: { processors: {}, products: [] }`)
273
- 3. Document in README.md Configuration section
61
+ ## Doc-update parity
274
62
 
275
- ### Payment Configuration
276
- Payment config shape mirrors OMEGA (the SSOT) — same key names used in BEM, UJM, and EM:
277
- - `processors`: Stripe, PayPal, Chargebee, Coinbase (publishable keys / client IDs)
278
- - `products`: Array of `{ id, name, type, limits: { feature: N }, prices, trial, paypal, stripe, chargebee }` — used to resolve usage limits on the frontend AND drive checkout flows
63
+ Whenever you make a behavioral change (new module, new method, new pattern, removed feature), update:
279
64
 
280
- ### Adding a Data Binding Action
281
- 1. Edit `_executeAction()` in `src/modules/bindings.js`
282
- 2. Add case for new action (e.g., `@class`)
283
- 3. Document in README.md Data Binding section
65
+ 1. **`README.md`** user-facing summary
66
+ 2. **`CLAUDE.md`** (this file) architecture overview, one paragraph or cross-link
67
+ 3. **`docs/<topic>.md`** the meat. If a topic doesn't have a doc yet, create one.
68
+ 4. **`CHANGELOG.md`** if the project keeps one
284
69
 
285
- ## Dependencies
70
+ Don't ship behavioral changes with stale docs. Validate first, then document — write docs that describe shipped reality, not intentions.
286
71
 
287
- | Package | Purpose |
288
- |---------|---------|
289
- | `firebase` (^12.x) | Auth, Firestore, Messaging |
290
- | `@sentry/browser` (^10.x) | Error tracking |
291
- | `lodash` (^4.x) | get/set for path-based access |
292
- | `itwcw-package-analytics` | Analytics (internal) |
72
+ ## Documentation
293
73
 
294
- ## Important Notes
74
+ Deep references live in `docs/`. Treat docs as a first-class deliverable. **Whenever you make a behavioral change, update both this overview AND the relevant `docs/*.md` deep reference.**
295
75
 
296
- 1. **DO NOT MODIFY `_legacy/`** - Reference only for historical context
297
- 2. **Backwards compatibility is NOT required** - Just change to the new way
298
- 3. **Prefer `fs-jetpack`** over `fs` for any file operations in tests/scripts
299
- 4. **No TypeScript** - This is a pure JavaScript library
300
- 5. **Template strings** - Use backticks for string interpolation
301
- 6. **Modular design** - Keep modules focused and small
76
+ - [docs/architecture.md](docs/architecture.md) singleton pattern, directory structure, module dependency graph
77
+ - [docs/code-patterns.md](docs/code-patterns.md) early returns, `$`-prefixed DOM vars, logical operator placement, Firestore path syntax, dynamic imports, config deep-merge, event delegation
78
+ - [docs/modules.md](docs/modules.md) full module quick reference (Storage, Auth + `resolveSubscription` + Settler Pattern, Bindings, Firestore, Notifications, ServiceWorker, Sentry, DOM, Utilities)
79
+ - [docs/build-system.md](docs/build-system.md) `prepare-package` ES5 transpile, build commands, package exports
80
+ - [docs/testing.md](docs/testing.md) Mocha test setup
81
+ - [docs/common-tasks.md](docs/common-tasks.md) adding a utility, adding a module, modifying config defaults, payment config (OMEGA SSOT shape), adding a binding action
82
+ - [docs/dependencies.md](docs/dependencies.md) — dependencies table + important notes (no TypeScript, prefer fs-jetpack, no backwards-compat requirement, etc.)
@@ -35,6 +35,17 @@ const DEFAULT_ACCOUNT = {
35
35
  affiliate: { code: null, timestamp: null, url: null, page: null },
36
36
  utm: { tags: {}, timestamp: null, url: null, page: null },
37
37
  },
38
+ consent: {
39
+ legal: {
40
+ status: 'revoked',
41
+ grantedAt: { timestamp: null, timestampUNIX: null, source: null, ip: null, text: null },
42
+ },
43
+ marketing: {
44
+ status: 'revoked',
45
+ grantedAt: { timestamp: null, timestampUNIX: null, source: null, ip: null, text: null },
46
+ revokedAt: { timestamp: null, timestampUNIX: null, source: null, ip: null, text: null },
47
+ },
48
+ },
38
49
  };
39
50
 
40
51
  function resolveAccount(rawData, firebaseUser) {
@@ -130,7 +130,7 @@ class Utilities {
130
130
 
131
131
  const $notification = document.createElement('div');
132
132
  $notification.className = `alert alert-${type} alert-dismissible fade show position-fixed`;
133
- $notification.style.cssText = 'z-index: 9999; top: 1rem; left: 50%; transform: translateX(-50%);';
133
+ $notification.style.cssText = 'z-index: 9999; top: 1rem; left: 50%; transform: translateX(-50%); width: calc(100% - 2rem); max-width: 500px;';
134
134
 
135
135
  const $text = document.createElement('span');
136
136
  $text.textContent = text;
@@ -0,0 +1,52 @@
1
+ # Architecture
2
+
3
+ ## Singleton Pattern
4
+
5
+ The library exports a singleton `Manager` instance. Import it directly from any file — it's always the same initialized instance:
6
+
7
+ ```javascript
8
+ import webManager from 'web-manager';
9
+
10
+ // Same instance everywhere — config, auth, firestore, all ready
11
+ webManager.auth().listen((state) => { ... });
12
+ webManager.utilities().escapeHTML(untrustedText);
13
+ webManager.config.environment; // 'development' or 'production'
14
+ ```
15
+
16
+ **Do NOT create new instances** (`new Manager()`). UJM and BXM initialize the singleton — every import gets that same object. Do NOT pass `webManager` through function params or store it in module-level variables — just import it.
17
+
18
+ ## Directory Structure
19
+
20
+ ```
21
+ web-manager/
22
+ ├── src/ # Source code (ES6+)
23
+ │ ├── index.js # Manager class, initialization, Firebase setup
24
+ │ └── modules/ # Feature modules
25
+ │ ├── auth.js # Firebase Auth wrapper
26
+ │ ├── bindings.js # Reactive DOM data binding
27
+ │ ├── dom.js # loadScript, ready utilities
28
+ │ ├── firestore.js # Firestore wrapper with chainable queries
29
+ │ ├── notifications.js # FCM push notifications
30
+ │ ├── sentry.js # Error tracking integration
31
+ │ ├── service-worker.js # SW registration and messaging
32
+ │ ├── storage.js # localStorage/sessionStorage wrapper
33
+ │ └── utilities.js # Helper functions (clipboard, escape, etc.)
34
+ ├── dist/ # Transpiled ES5 output (generated)
35
+ ├── _legacy/ # Old implementation (reference only, DO NOT MODIFY)
36
+ └── test/ # Mocha tests
37
+ ```
38
+
39
+ ## Module Dependencies
40
+
41
+ ```
42
+ Manager (index.js)
43
+ ├── Storage (standalone, no deps)
44
+ ├── Auth → Manager, Bindings, Storage, Firestore
45
+ ├── Bindings → Manager
46
+ ├── Firestore → Manager (lazy Firebase import)
47
+ ├── Notifications → Manager, Storage, Firestore
48
+ ├── ServiceWorker → Manager
49
+ ├── Sentry → Manager (dynamic import)
50
+ ├── DOM utilities (standalone)
51
+ └── Utilities (standalone)
52
+ ```
@@ -0,0 +1,32 @@
1
+ # Build System
2
+
3
+ ## prepare-package
4
+
5
+ The library uses `prepare-package` for ES5 transpilation:
6
+
7
+ ```json
8
+ {
9
+ "preparePackage": {
10
+ "input": "./src",
11
+ "output": "./dist"
12
+ }
13
+ }
14
+ ```
15
+
16
+ **Commands**:
17
+ - `npm run prepare` — Build once
18
+ - `npm start` — Watch mode
19
+ - `npm test` — Run Mocha tests
20
+
21
+ ## Package Exports
22
+
23
+ ```json
24
+ {
25
+ "main": "dist/index.js",
26
+ "module": "src/index.js",
27
+ "exports": {
28
+ ".": "./dist/index.js",
29
+ "./modules/*": "./dist/modules/*"
30
+ }
31
+ }
32
+ ```
@@ -0,0 +1,93 @@
1
+ # Key Patterns
2
+
3
+ ## 1. Early Return (Short-Circuit)
4
+
5
+ Always use early returns instead of nested conditionals:
6
+
7
+ ```javascript
8
+ // CORRECT
9
+ function doSomething() {
10
+ if (!condition) {
11
+ return;
12
+ }
13
+ // Long code block...
14
+ }
15
+
16
+ // WRONG
17
+ function doSomething() {
18
+ if (condition) {
19
+ // Long code block...
20
+ }
21
+ }
22
+ ```
23
+
24
+ ## 2. DOM Element Naming
25
+
26
+ Prefix DOM element variables with `$`:
27
+
28
+ ```javascript
29
+ const $button = document.querySelector('.submit-btn');
30
+ const $input = document.getElementById('email');
31
+ ```
32
+
33
+ ## 3. Logical Operator Formatting
34
+
35
+ Place operators at the START of continuation lines:
36
+
37
+ ```javascript
38
+ // CORRECT
39
+ const result = conditionA
40
+ || conditionB
41
+ || conditionC;
42
+
43
+ // WRONG
44
+ const result = conditionA ||
45
+ conditionB ||
46
+ conditionC;
47
+ ```
48
+
49
+ ## 4. Firestore Path Syntax
50
+
51
+ Prefer path syntax over collection/doc chaining:
52
+
53
+ ```javascript
54
+ // PREFERRED
55
+ db.doc('users/userId')
56
+
57
+ // ALSO SUPPORTED
58
+ db.doc('users', 'userId')
59
+ ```
60
+
61
+ ## 5. Dynamic Imports
62
+
63
+ Firebase modules are dynamically imported to reduce bundle size:
64
+
65
+ ```javascript
66
+ const { initializeApp } = await import('firebase/app');
67
+ const { getAuth } = await import('firebase/auth');
68
+ ```
69
+
70
+ ## 6. Configuration Deep Merge
71
+
72
+ User config is deep-merged with defaults in `_processConfiguration()`. Only override what you need:
73
+
74
+ ```javascript
75
+ // Defaults defined in _processConfiguration()
76
+ const defaults = {
77
+ environment: 'production',
78
+ firebase: { app: { enabled: true, config: {} } },
79
+ // ...
80
+ };
81
+ ```
82
+
83
+ ## 7. Event Delegation
84
+
85
+ Auth UI uses event delegation on document body:
86
+
87
+ ```javascript
88
+ document.body.addEventListener('click', (e) => {
89
+ if (e.target.closest('.auth-signout-btn')) {
90
+ // Handle signout
91
+ }
92
+ });
93
+ ```
@@ -0,0 +1,36 @@
1
+ # Common Tasks
2
+
3
+ ## Adding a New Utility Function
4
+
5
+ 1. Add function to `src/modules/utilities.js`
6
+ 2. Export it: `export function myFunction() { ... }`
7
+ 3. Update README.md with documentation
8
+ 4. Run `npm run prepare` to build
9
+
10
+ ## Adding a New Module
11
+
12
+ 1. Create `src/modules/my-module.js`
13
+ 2. Export class: `export default class MyModule { constructor(manager) { ... } }`
14
+ 3. Import in `src/index.js`: `import MyModule from './modules/my-module.js'`
15
+ 4. Add to Manager constructor: `this._myModule = new MyModule(this)`
16
+ 5. Add getter: `myModule() { return this._myModule; }`
17
+ 6. Update README.md
18
+ 7. Run `npm run prepare`
19
+
20
+ ## Modifying Configuration Defaults
21
+
22
+ 1. Edit `_processConfiguration()` in `src/index.js`
23
+ 2. Add to `defaults` object (e.g., `payment: { processors: {}, products: [] }`)
24
+ 3. Document in README.md Configuration section
25
+
26
+ ## Payment Configuration
27
+
28
+ Payment config shape mirrors OMEGA (the SSOT) — same key names used in BEM, UJM, and EM:
29
+ - `processors`: Stripe, PayPal, Chargebee, Coinbase (publishable keys / client IDs)
30
+ - `products`: Array of `{ id, name, type, limits: { feature: N }, prices, trial, paypal, stripe, chargebee }` — used to resolve usage limits on the frontend AND drive checkout flows
31
+
32
+ ## Adding a Data Binding Action
33
+
34
+ 1. Edit `_executeAction()` in `src/modules/bindings.js`
35
+ 2. Add case for new action (e.g., `@class`)
36
+ 3. Document in README.md Data Binding section
@@ -0,0 +1,19 @@
1
+ # Dependencies & Important Notes
2
+
3
+ ## Dependencies
4
+
5
+ | Package | Purpose |
6
+ |---------|---------|
7
+ | `firebase` (^12.x) | Auth, Firestore, Messaging |
8
+ | `@sentry/browser` (^10.x) | Error tracking |
9
+ | `lodash` (^4.x) | get/set for path-based access |
10
+ | `itwcw-package-analytics` | Analytics (internal) |
11
+
12
+ ## Important Notes
13
+
14
+ 1. **DO NOT MODIFY `_legacy/`** — Reference only for historical context
15
+ 2. **Backwards compatibility is NOT required** — Just change to the new way
16
+ 3. **Prefer `fs-jetpack`** over `fs` for any file operations in tests/scripts
17
+ 4. **No TypeScript** — This is a pure JavaScript library
18
+ 5. **Template strings** — Use backticks for string interpolation
19
+ 6. **Modular design** — Keep modules focused and small
@@ -0,0 +1,80 @@
1
+ # Module Quick Reference
2
+
3
+ ## Storage (`storage.js`)
4
+
5
+ - **Class**: `Storage`
6
+ - **Key Methods**: `get(path, default)`, `set(path, value)`, `remove(path)`, `clear()`
7
+ - **Session**: Same methods under `.session` namespace
8
+ - **Storage Key**: `_manager` in localStorage
9
+
10
+ ## Auth (`auth.js`)
11
+
12
+ - **Class**: `Auth`
13
+ - **Key Methods**: `listen(options, callback)`, `isAuthenticated()`, `getUser()`, `signInWithEmailAndPassword()`, `signOut()`, `getIdToken()`, `resolveSubscription(account?)`
14
+ - **Bindings**: Updates `auth` and `usage` context on auth settle
15
+ - **Usage Resolution**: `_resolveUsage(state)` merges `account.usage` (Firestore) with product limits from `config.payment.products` (OMEGA-canonical shape — same key name in BEM, UJM, and EM) to produce the `usage` bindings key (e.g., `{ credits: { monthly: 5, limit: 100 } }`)
16
+
17
+ ### resolveSubscription(account?)
18
+
19
+ Derives calculated subscription fields from raw account data. Returns only fields that require derivation logic — raw data (product.id, status, trial, cancellation) lives on `account.subscription` directly.
20
+
21
+ ```javascript
22
+ const resolved = auth.resolveSubscription(account);
23
+ // Returns: { plan, active, trialing, cancelling }
24
+ ```
25
+
26
+ - `plan`: Effective plan ID the user has access to RIGHT NOW (`'basic'` if cancelled/suspended)
27
+ - `active`: User has active access (active, trialing, or cancelling — all mean the user can use the product)
28
+ - `trialing`: In an active trial (status `'active'` + `trial.claimed` + unexpired `trial.expires`)
29
+ - `cancelling`: Cancellation pending (status `'active'` + `cancellation.pending` + NOT trialing)
30
+
31
+ **Unified with BEM**: The same function exists on `User.resolveSubscription(account)` in backend-manager (`helpers/user.js`) with identical logic and return shape.
32
+
33
+ ### Auth Settler Pattern
34
+
35
+ Auth uses a promise-based settler (`_authReady`) that resolves once Firebase's first `onAuthStateChanged` fires — the moment auth state is guaranteed (authenticated user OR null). This eliminates race conditions.
36
+
37
+ - **`once` listeners** (`listen({ once: true }, cb)`): Wait for `_authReady`, fire once, done. No cleanup needed.
38
+ - **Persistent listeners** (`listen({}, cb)`): Subscribe to `_authStateCallbacks`. If auth already settled when registered, catch up via `_authReady.then()`. Otherwise, `_handleAuthStateChange` handles the initial call naturally.
39
+ - **`_hasProcessedStateChange`**: Ensures bindings/storage updates run only once per auth state change across all listeners.
40
+ - **Manager owns the promise**: `_authReady` and `_authReadyResolve` live on the Manager instance. The `onAuthStateChanged` callback in `index.js` resolves it on first fire and sets `_firebaseAuthInitialized = true`.
41
+
42
+ ## Bindings (`bindings.js`)
43
+
44
+ - **Class**: `Bindings`
45
+ - **Key Methods**: `update(data)`, `getContext()`, `clear()`
46
+ - **HTML Attr**: `data-wm-bind`
47
+ - **Actions**: `@text`, `@value`, `@show`, `@hide`, `@attr`, `@style`
48
+
49
+ ## Firestore (`firestore.js`)
50
+
51
+ - **Class**: `Firestore`
52
+ - **Key Methods**: `doc(path)`, `collection(path)`
53
+ - **Doc Methods**: `.get()`, `.set()`, `.update()`, `.delete()`
54
+ - **Query Methods**: `.where()`, `.orderBy()`, `.limit()`, `.startAt()`, `.endAt()`
55
+
56
+ ## Notifications (`notifications.js`)
57
+
58
+ - **Class**: `Notifications`
59
+ - **Key Methods**: `isSupported()`, `isSubscribed()`, `subscribe()`, `unsubscribe()`, `getToken()`, `onMessage()`
60
+ - **Storage**: Saves to localStorage and Firestore
61
+
62
+ ## ServiceWorker (`service-worker.js`)
63
+
64
+ - **Class**: `ServiceWorker`
65
+ - **Key Methods**: `isSupported()`, `register()`, `ready()`, `postMessage()`, `onMessage()`, `getState()`
66
+
67
+ ## Sentry (`sentry.js`)
68
+
69
+ - **Class**: `Sentry` (named `mod` internally)
70
+ - **Key Methods**: `init(config)`, `captureException(error, context)`
71
+ - **Filtering**: Blocks dev mode, Lighthouse, Selenium/Puppeteer
72
+
73
+ ## DOM (`dom.js`)
74
+
75
+ - **Exports**: `loadScript(options)`, `ready()`
76
+ - **loadScript Options**: src, async, defer, crossorigin, integrity, timeout, retries
77
+
78
+ ## Utilities (`utilities.js`)
79
+
80
+ - **Exports**: `clipboardCopy()`, `escapeHTML()`, `showNotification()`, `getPlatform()`, `getBrowser()`, `getRuntime()`, `isMobile()`, `getDevice()`, `getContext()`
@@ -0,0 +1,9 @@
1
+ # Testing
2
+
3
+ Tests are in `test/test.js` using Mocha:
4
+
5
+ ```bash
6
+ npm test
7
+ ```
8
+
9
+ Current test coverage is minimal — focuses on configuration and storage.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "web-manager",
3
- "version": "4.1.42",
3
+ "version": "4.2.0",
4
4
  "description": "Easily access important variables such as the query string, current domain, and current page in a single object.",
5
5
  "main": "dist/index.js",
6
6
  "module": "src/index.js",