web-manager 4.3.1 → 4.3.2

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,15 @@ 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.3.2] - 2026-06-11
19
+
20
+ ### Added
21
+ - **`docs/bindings.md`**: full `data-wm-bind` deep reference migrated from the `omega:ujm` skill into the repo (the module that implements the feature) — actions table, comma-separated multi-binding syntax + parser caveat, condition operators, auth/usage/custom state paths, JS API, skeleton-loader lifecycle (`wm-binding-skeleton` → `wm-bound`), multi-phase binding, composite-text pattern, and root-key update filtering. Linked from CLAUDE.md and `docs/modules.md`. Part of the skills-as-routers refactor: framework facts live in repo docs (version-matched via `node_modules`).
22
+
23
+ ### Changed
24
+ - **Dependency bumps**: `@sentry/browser` `^10.54.0` → `^10.57.0`, `firebase` `^12.13.0` → `^12.14.0`.
25
+
17
26
  ---
18
27
  ## [4.2.0] - 2026-05-21
19
28
  ### Added
package/CLAUDE.md CHANGED
@@ -76,6 +76,7 @@ Deep references live in `docs/`. Treat docs as a first-class deliverable. **When
76
76
  - [docs/architecture.md](docs/architecture.md) — singleton pattern, directory structure, module dependency graph
77
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
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/bindings.md](docs/bindings.md) — `data-wm-bind` deep reference: actions, comma syntax, condition operators, state paths, skeleton loaders, root-key update filtering
79
80
  - [docs/build-system.md](docs/build-system.md) — `prepare-package` ES5 transpile, build commands, package exports
80
81
  - [docs/testing.md](docs/testing.md) — Mocha test setup
81
82
  - [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
package/TODO.md CHANGED
@@ -1,5 +1,10 @@
1
- BRING BACK THESE THINGS FROM LEGACY:
2
- - Add back build.json fetch
1
+ RADOM
2
+ Remove this remove this eventually once we test the VAPDI push notificationx issue
3
+
4
+
5
+ const tokenOptions = { serviceWorkerRegistration: swRegistration };
6
+ if (this._vapidKey) { tokenOptions.vapidKey = this._vapidKey; }
7
+ return await getToken(messaging, tokenOptions);
3
8
 
4
9
  Do we need to use polyfill? the project that consumes this is using webpack and compiles to es5. we need to ensure we have
5
10
  - fetch api
@@ -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
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,6 +1,6 @@
1
1
  {
2
2
  "name": "web-manager",
3
- "version": "4.3.1",
3
+ "version": "4.3.2",
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",
@@ -43,9 +43,9 @@
43
43
  "@sentry/browser": "Resolved by using OVERRIDES in web-manager (lighthouse is the issue"
44
44
  },
45
45
  "dependencies": {
46
- "@sentry/browser": "^10.54.0",
46
+ "@sentry/browser": "^10.57.0",
47
47
  "chatsy": "^2.0.14",
48
- "firebase": "^12.13.0",
48
+ "firebase": "^12.14.0",
49
49
  "itwcw-package-analytics": "^1.0.8",
50
50
  "lodash": "^4.18.1"
51
51
  },