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 +9 -0
- package/CLAUDE.md +1 -0
- package/TODO.md +7 -2
- package/docs/bindings.md +223 -0
- package/docs/modules.md +1 -0
- package/package.json +3 -3
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
|
-
|
|
2
|
-
|
|
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
|
package/docs/bindings.md
ADDED
|
@@ -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"> </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"> </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 ` ` 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"> </span>
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Do NOT use text like "Loading..." as placeholder** — it flashes visible text before the shimmer kicks in. Use ` ` 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"> </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.
|
|
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.
|
|
46
|
+
"@sentry/browser": "^10.57.0",
|
|
47
47
|
"chatsy": "^2.0.14",
|
|
48
|
-
"firebase": "^12.
|
|
48
|
+
"firebase": "^12.14.0",
|
|
49
49
|
"itwcw-package-analytics": "^1.0.8",
|
|
50
50
|
"lodash": "^4.18.1"
|
|
51
51
|
},
|