web-manager 4.3.1 → 4.3.3

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 CHANGED
@@ -69,6 +69,8 @@ Whenever you make a behavioral change (new module, new method, new pattern, remo
69
69
 
70
70
  Don't ship behavioral changes with stale docs. Validate first, then document — write docs that describe shipped reality, not intentions.
71
71
 
72
+ **The OMEGA docs are structurally MIRRORED.** WM follows the library subset of the canonical OMEGA CLAUDE.md skeleton (the scaffolding frameworks UJM / BEM / BXM / EM / MAM carry the full skeleton + a consumer template). Never add, rename, or reorder a section here without checking the sister repos and the canonical skeletons + omission rules in the `omega:main` skill's `mirror-spec.md` resource.
73
+
72
74
  ## Documentation
73
75
 
74
76
  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.**
@@ -76,7 +78,9 @@ Deep references live in `docs/`. Treat docs as a first-class deliverable. **When
76
78
  - [docs/architecture.md](docs/architecture.md) — singleton pattern, directory structure, module dependency graph
77
79
  - [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
80
  - [docs/modules.md](docs/modules.md) — full module quick reference (Storage, Auth + `resolveSubscription` + Settler Pattern, Bindings, Firestore, Notifications, ServiceWorker, Sentry, DOM, Utilities)
81
+ - [docs/bindings.md](docs/bindings.md) — `data-wm-bind` deep reference: actions, comma syntax, condition operators, state paths, skeleton loaders, root-key update filtering
79
82
  - [docs/build-system.md](docs/build-system.md) — `prepare-package` ES5 transpile, build commands, package exports
80
83
  - [docs/testing.md](docs/testing.md) — Mocha test setup
84
+ - [docs/cdp-debugging.md](docs/cdp-debugging.md) — driving a live browser (per-session isolated Chrome via the `chrome-devtools` MCP) to verify WM inside a consuming site
81
85
  - [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
86
  - [docs/dependencies.md](docs/dependencies.md) — dependencies table + important notes (no TypeScript, prefer fs-jetpack, no backwards-compat requirement, etc.)
@@ -0,0 +1,223 @@
1
+ # Bindings (`data-wm-bind`)
2
+
3
+ The `data-wm-bind` attribute declaratively binds DOM elements to state data (auth, plan, roles, usage, custom state) managed by `webManager.bindings()`. **Always prefer wm-bindings over manual JS class toggling** for anything based on user/auth state — if an element's visibility or content depends on the user object, use `data-wm-bind` in HTML, not `classList.toggle('d-none', ...)` or `.hidden` from JS.
4
+
5
+ ## HTML Syntax
6
+
7
+ ```html
8
+ <element data-wm-bind="@action path.to.data"></element>
9
+ ```
10
+
11
+ ### Multiple Bindings: MUST Use Commas
12
+
13
+ Multiple bindings are **comma-separated**. The parser splits by comma first, then parses each part as `@action expression`.
14
+
15
+ ```html
16
+ <!-- CORRECT: comma-separated -->
17
+ <element data-wm-bind="@show auth.user, @attr src auth.user.photoURL"></element>
18
+
19
+ <!-- WRONG: space-separated — gets parsed as ONE binding with action=@show, expression="auth.user @attr src auth.user.photoURL" -->
20
+ <element data-wm-bind="@show auth.user @attr src auth.user.photoURL"></element>
21
+ ```
22
+
23
+ **Why this matters:** The parser splits on `,` then finds the first space within each part to separate `@action` from `expression`. Without commas, everything after the first `@action` is treated as a single expression string, producing broken behavior with no error.
24
+
25
+ ## Supported Actions
26
+
27
+ | Action | Syntax | Description |
28
+ |--------|--------|-------------|
29
+ | `@text` | `@text path` | Set text content |
30
+ | `@value` | `@value path` | Set input/textarea value |
31
+ | `@show` | `@show condition` | Show element if truthy |
32
+ | `@hide` | `@hide condition` | Hide element if truthy |
33
+ | `@attr` | `@attr name path` | Set HTML attribute value |
34
+ | `@style` | `@style property path` | Set CSS property or CSS variable |
35
+
36
+ ## Condition Operators
37
+
38
+ ```html
39
+ <!-- Truthy check -->
40
+ <div data-wm-bind="@show auth.user">Visible when logged in</div>
41
+
42
+ <!-- Negation (!) -->
43
+ <div data-wm-bind="@show !auth.user">Visible when NOT logged in</div>
44
+
45
+ <!-- Comparisons (===, !==, ==, !=, >, <, >=, <=) -->
46
+ <div data-wm-bind="@show auth.account.plan.id === 'premium'">Premium only</div>
47
+ <div data-wm-bind="@show checkout.errorCount > 0">Has errors</div>
48
+ ```
49
+
50
+ No logic operators (`&&`, `||`) in conditions — keep conditions simple. Right-side comparison values are auto-parsed: quoted strings, numbers, booleans, null.
51
+
52
+ ## Common Auth Patterns
53
+
54
+ ```html
55
+ <!-- Show for anonymous users -->
56
+ <div data-wm-bind="@show !auth.user">
57
+ <a href="/signup">Create free account</a>
58
+ </div>
59
+
60
+ <!-- Show for signed-in users -->
61
+ <div data-wm-bind="@show auth.user">
62
+ <a href="/pricing">Upgrade your plan</a>
63
+ </div>
64
+
65
+ <!-- Admin-only elements -->
66
+ <div data-wm-bind="@show auth.account.roles.admin">Admin panel</div>
67
+
68
+ <!-- User data binding -->
69
+ <img data-wm-bind="@show auth.user, @attr src auth.user.photoURL, @attr alt auth.user.displayName">
70
+ <span data-wm-bind="@text auth.user.displayName">Loading...</span>
71
+ <input data-wm-bind="@value auth.user.email">
72
+ ```
73
+
74
+ ## Available State Paths
75
+
76
+ ### Auth paths (automatically populated by web-manager)
77
+
78
+ ```
79
+ auth.user # Firebase user object (truthy = signed in)
80
+ auth.user.uid # User ID
81
+ auth.user.email # Email
82
+ auth.user.displayName # Display name
83
+ auth.user.photoURL # Avatar URL
84
+ auth.user.emailVerified # Boolean
85
+ auth.account.plan.id # Plan ID (e.g. 'basic', 'premium')
86
+ auth.account.roles.admin # Boolean
87
+ auth.account.roles.betaTester # Boolean
88
+ ```
89
+
90
+ ### Usage paths (auto-populated by web-manager + authorized-fetch)
91
+
92
+ ```
93
+ usage.{feature}.monthly # Current monthly usage count
94
+ usage.{feature}.daily # Current daily usage count
95
+ usage.{feature}.limit # Plan limit for this feature
96
+ ```
97
+
98
+ Example: `usage.credits.monthly`, `usage.credits.limit`
99
+
100
+ Seeded on auth settle from `account.usage` + the site's payment plan config. Refreshed after every `authorizedFetch` call from `bm-properties` response headers.
101
+
102
+ ### Custom state (set via JS)
103
+
104
+ Any custom paths set via `webManager.bindings().update(stateObject)`.
105
+
106
+ ## JavaScript API
107
+
108
+ ```javascript
109
+ // Update bindings with state data
110
+ webManager.bindings().update({
111
+ checkout: {
112
+ product: { name: 'Pro Plan' },
113
+ error: { show: false, message: '' },
114
+ },
115
+ });
116
+
117
+ // Get current binding context
118
+ const context = webManager.bindings().getContext();
119
+
120
+ // Clear all bindings
121
+ webManager.bindings().clear();
122
+ ```
123
+
124
+ ## Skeleton Loaders
125
+
126
+ ### How Skeletons Work
127
+
128
+ The `wm-binding-skeleton` class shows a shimmer animation until data loads. Skeletons resolve **only when at least one of the element's bindings is actually processed** — meaning the binding's root key must be in the `updatedKeys` for that `update()` call.
129
+
130
+ When `_updateBindings` processes an element:
131
+
132
+ 1. Executes each binding action (`@text`, `@show`, `@attr`, etc.)
133
+ 2. Each action returns `true` (processed) or `false` (skipped because root key wasn't updated)
134
+ 3. **Only if at least one action was processed:** adds `wm-bound` class (triggers CSS fade-out transition)
135
+ 4. After 300ms, removes `wm-binding-skeleton` class (shimmer disappears)
136
+
137
+ **Root key scoping matters for skeletons.** If an element is bound to `checkout.pricing.total` and `update({ auth: ... })` fires, that element's skeleton is NOT resolved — the binding is skipped entirely because `checkout` is not in `updatedKeys`.
138
+
139
+ ```html
140
+ <!-- Skeleton resolves when 'auth' key is updated -->
141
+ <span class="wm-binding-skeleton" data-wm-bind="@text auth.user.displayName">&nbsp;</span>
142
+
143
+ <!-- Skeleton resolves when 'checkout' key is updated (NOT when 'auth' updates) -->
144
+ <span class="wm-binding-skeleton" data-wm-bind="@text checkout.pricing.total">&nbsp;</span>
145
+ ```
146
+
147
+ ### Multi-phase binding example (e.g., checkout page)
148
+
149
+ When bindings fire in phases, skeletons resolve independently per root key:
150
+
151
+ ```javascript
152
+ // Phase 1: Global auth bindings fire
153
+ webManager.bindings().update({ auth: { user: {...} } });
154
+ // → Only elements bound to 'auth.*' resolve their skeletons
155
+ // → Elements bound to 'checkout.*' keep their skeletons
156
+
157
+ // Phase 2: After API fetches complete
158
+ webManager.bindings().update({ checkout: { pricing: {...} } });
159
+ // → Now elements bound to 'checkout.*' resolve their skeletons
160
+ ```
161
+
162
+ This prevents checkout skeletons from disappearing prematurely when global auth bindings fire before checkout data is available.
163
+
164
+ ### Skeleton Pattern
165
+
166
+ Use `&nbsp;` as placeholder content (prevents zero-width collapse so the shimmer is visible):
167
+
168
+ ```html
169
+ <span class="wm-binding-skeleton" data-wm-bind="@text auth.user.displayName">&nbsp;</span>
170
+ ```
171
+
172
+ **Do NOT use text like "Loading..." as placeholder** — it flashes visible text before the shimmer kicks in. Use `&nbsp;` for a clean shimmer-only experience.
173
+
174
+ ### Composite Text in Skeletons
175
+
176
+ For composite text (e.g., "$0.00 due today"), do NOT mix static text with a binding span inside a skeleton div. Instead, create a dedicated pre-formatted value in the state and bind with a single `@text`:
177
+
178
+ ```javascript
179
+ // CORRECT: compose the text in JS, bind as single value
180
+ webManager.bindings().update({
181
+ checkout: {
182
+ totalDueText: `${formatCurrency(prices.total)} due today`,
183
+ },
184
+ });
185
+ ```
186
+
187
+ ```html
188
+ <!-- CORRECT: single binding for composite text -->
189
+ <span class="wm-binding-skeleton" data-wm-bind="@text checkout.totalDueText">&nbsp;</span>
190
+
191
+ <!-- WRONG: mixing static text with binding inside skeleton -->
192
+ <span class="wm-binding-skeleton">
193
+ <span data-wm-bind="@text checkout.pricing.total"></span> due today
194
+ </span>
195
+ ```
196
+
197
+ ## Implementation Notes
198
+
199
+ - Uses the `hidden` attribute for show/hide (`[hidden] { display: none !important; }`)
200
+ - Queries `[data-wm-bind]` on each `update()` call — handles dynamic elements
201
+ - Auth bindings are auto-populated when `webManager.auth().listen()` fires
202
+ - When `updatedKeys` is `null` (e.g., from `clear()`), ALL bindings fire
203
+
204
+ ### Root Key Update Filtering
205
+
206
+ `_shouldUpdatePath` checks the **root key** (first segment before `.`) of each binding's expression path against the `updatedKeys` from the `update()` call. A binding only fires when its root key was updated.
207
+
208
+ ```javascript
209
+ // This update ONLY triggers bindings whose expression starts with 'checkout'
210
+ webManager.bindings().update({
211
+ checkout: { pricing: { total: 9.99 } },
212
+ });
213
+ // Fires: @text checkout.pricing.total, @show checkout.active
214
+ // Skips: @text auth.user.displayName (root key is 'auth', not updated)
215
+ ```
216
+
217
+ Negation (`!`) is stripped before root-key checking, so `@show !auth.user` fires when `auth` is updated.
218
+
219
+ ## See also
220
+
221
+ - [modules.md](modules.md) — quick reference for all nine modules
222
+ - [architecture.md](architecture.md) — module dependency graph
223
+ - `src/modules/bindings.js` — the implementation
@@ -0,0 +1,29 @@
1
+ # CDP Debugging (driving a live browser)
2
+
3
+ How to drive a browser you can CONTROL — see a consuming site live, screenshot it, click, type, read console logs, inspect network requests — for agents (Claude via MCP/CDP) and humans. WM has no dev server of its own; it runs INSIDE consumers (UJM sites, BXM extensions, EM renderers), so browser verification means driving a consumer.
4
+
5
+ > Mirrored across the five sister frameworks (UJM / BEM / BXM / EM / WM) — same core section, framework-flavored. Edit all five together.
6
+
7
+ ## The browser: your Claude session owns one
8
+
9
+ Browser work runs through the **`chrome-devtools` MCP** (via mcp-router). There is NO launch procedure anymore — no ports, no profile dirs, no curl checks:
10
+
11
+ - **Just call the tools** — `new_page`, `navigate_page`, `take_screenshot`, `click`, `fill`, `evaluate_script`, `list_console_messages`, `list_network_requests`. The browser auto-launches on the first call.
12
+ - **Each Claude session gets its OWN private Chrome** (`--isolated`): temp profile, CDP over an internal pipe. Parallel sessions cannot see or touch each other's pages — open and close pages freely, the whole browser is yours.
13
+ - **It dies with the session.** No orphans, no cleanup, nothing to kill.
14
+ - **Ephemeral profile** — cookies/logins do NOT persist between sessions. If a flow needs auth, log in during the task.
15
+ - **Self-signed HTTPS is pre-accepted** (`--acceptInsecureCerts` in the upstream) — dev servers load without certificate interstitials.
16
+ - **NEVER quit/kill Chrome by app name** (`killall "Google Chrome"`, osascript) — that's the user's personal browser, not yours.
17
+
18
+ Humans: the agent's Chrome window is visible — you can watch it drive. Full reference: `~/.claude/mcp-server/servers/chrome-devtools/CLAUDE.md`.
19
+
20
+ ## Electron apps are the exception (attach, don't launch)
21
+
22
+ An Electron dev app is a running singleton — you ATTACH to it instead of launching a browser: the `chrome-devtools-electron` MCP upstream (reads `EM_CDP_PORT`, default 9222, expanded once at session start) or EM's per-invocation `npx mgr cdp`. See EM's `docs/cdp-debugging.md`.
23
+
24
+ ## WM specifics
25
+
26
+ - **Verify WM behavior through a consumer.** The usual host is a UJM site's dev server: **`https://localhost:4000` — NEVER the LAN IP** (`https://192.168.x.x:...`); port 4000 by default, increments (4001, …) when multiple sites run — exact port in the WEBSITE project's `.temp/_config_browsersync.yml`. To test uncommitted WM changes, link the local web-manager into the consumer first (see the consumer framework's dev-install flow), then drive the site.
27
+ - What to exercise from the browser: auth flows (`webManager.auth()` states, the Settler Pattern), `data-wm-bind` bindings reacting to state changes (`evaluate_script` to mutate state, `take_snapshot`/`take_screenshot` to verify DOM), Firestore reads/writes on the network tab, and console cleanliness (WM logs its module lifecycle).
28
+ - Ephemeral profile ⇒ auth'd testing means logging in through the consumer's real UI at the start of the session (test creds).
29
+ - WM inside a BXM extension or EM renderer: drive those through their own surfaces — BXM's `chrome-devtools-extension` upstream, EM's `chrome-devtools-electron`/`mgr cdp` (see those repos' `docs/cdp-debugging.md`).
package/docs/modules.md CHANGED
@@ -45,6 +45,7 @@ Auth uses a promise-based settler (`_authReady`) that resolves once Firebase's f
45
45
  - **Key Methods**: `update(data)`, `getContext()`, `clear()`
46
46
  - **HTML Attr**: `data-wm-bind`
47
47
  - **Actions**: `@text`, `@value`, `@show`, `@hide`, `@attr`, `@style`
48
+ - **Deep reference**: [bindings.md](bindings.md) — comma syntax, condition operators, state paths, skeleton loaders, root-key filtering
48
49
 
49
50
  ## Firestore (`firestore.js`)
50
51
 
package/package.json CHANGED
@@ -1,8 +1,14 @@
1
1
  {
2
2
  "name": "web-manager",
3
- "version": "4.3.1",
3
+ "version": "4.3.3",
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
+ "files": [
7
+ "dist/",
8
+ "src/",
9
+ "docs/",
10
+ "CLAUDE.md"
11
+ ],
6
12
  "module": "src/index.js",
7
13
  "exports": {
8
14
  ".": "./dist/index.js",
@@ -43,9 +49,9 @@
43
49
  "@sentry/browser": "Resolved by using OVERRIDES in web-manager (lighthouse is the issue"
44
50
  },
45
51
  "dependencies": {
46
- "@sentry/browser": "^10.54.0",
52
+ "@sentry/browser": "^10.57.0",
47
53
  "chatsy": "^2.0.14",
48
- "firebase": "^12.13.0",
54
+ "firebase": "^12.14.0",
49
55
  "itwcw-package-analytics": "^1.0.8",
50
56
  "lodash": "^4.18.1"
51
57
  },