web-manager 4.1.23 → 4.1.25

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,16 @@ 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.1.25] - 2026-03-13
19
+ ### Added
20
+ - Automatically attach resolved subscription state (`state.resolved`) to auth state during processing, making `plan`, `active`, `trialing`, and `cancelling` available to bindings and consumers without manual calls.
21
+
22
+ ---
23
+ ## [4.1.24] - 2026-03-13
24
+ ### Added
25
+ - Added `resolveSubscription()` method to Auth module that derives calculated subscription fields (plan, active, trialing, cancelling) from raw backend data.
26
+
17
27
  ---
18
28
  ## [4.1.22] - 2026-03-11
19
29
  ### Changed
package/CLAUDE.md CHANGED
@@ -147,9 +147,23 @@ document.body.addEventListener('click', (e) => {
147
147
 
148
148
  ### Auth (`auth.js`)
149
149
  - **Class**: `Auth`
150
- - **Key Methods**: `listen(options, callback)`, `isAuthenticated()`, `getUser()`, `signInWithEmailAndPassword()`, `signOut()`, `getIdToken()`
150
+ - **Key Methods**: `listen(options, callback)`, `isAuthenticated()`, `getUser()`, `signInWithEmailAndPassword()`, `signOut()`, `getIdToken()`, `resolveSubscription(account?)`
151
151
  - **Bindings**: Updates `auth.user` and `auth.account` context
152
152
 
153
+ #### resolveSubscription(account?)
154
+ 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.
155
+
156
+ ```javascript
157
+ const resolved = auth.resolveSubscription(account);
158
+ // Returns: { plan, active, trialing, cancelling }
159
+ ```
160
+ - `plan`: Effective plan ID the user has access to RIGHT NOW (`'basic'` if cancelled/suspended)
161
+ - `active`: User has active access (active, trialing, or cancelling — all mean the user can use the product)
162
+ - `trialing`: In an active trial (status `'active'` + `trial.claimed` + unexpired `trial.expires`)
163
+ - `cancelling`: Cancellation pending (status `'active'` + `cancellation.pending` + NOT trialing)
164
+
165
+ **Unified with BEM**: The same function exists on `User.resolveSubscription(account)` in backend-manager (`helpers/user.js`) with identical logic and return shape.
166
+
153
167
  #### Auth Settler Pattern
154
168
  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.
155
169
 
package/README.md CHANGED
@@ -360,6 +360,50 @@ On page load, Firebase Auth takes time to restore the user session. The auth set
360
360
  **HTML Auth Classes**:
361
361
  - `.auth-signout-btn` - Sign out button (shows confirmation dialog)
362
362
 
363
+ **Resolve Subscription State**:
364
+
365
+ Derives calculated subscription fields from raw account data. Returns only fields that require logic — raw data is on `account.subscription` directly.
366
+
367
+ ```javascript
368
+ const resolved = auth.resolveSubscription(account);
369
+ // Or without an argument (falls back to stored auth state):
370
+ const resolved = auth.resolveSubscription();
371
+ ```
372
+
373
+ When called without an argument, reads the account from `localStorage` (the last auth state saved by `listen()`). Pass an explicit account when you have one to avoid stale data.
374
+
375
+ Returns:
376
+ ```javascript
377
+ {
378
+ plan: 'basic', // Effective plan ID right now ('basic' if cancelled/suspended)
379
+ active: true, // Has active access (active, trialing, or cancelling)
380
+ trialing: false, // In an active trial (status 'active' + unexpired trial)
381
+ cancelling: false, // Cancellation pending (status 'active' + cancellation.pending)
382
+ }
383
+ ```
384
+
385
+ Usage:
386
+ ```javascript
387
+ auth.listen({ once: true }, (state) => {
388
+ const resolved = auth.resolveSubscription(state.account);
389
+
390
+ if (!resolved.active) {
391
+ // User is on free plan or subscription ended
392
+ }
393
+
394
+ if (resolved.trialing) {
395
+ // Show trial banner
396
+ }
397
+
398
+ if (resolved.cancelling) {
399
+ // Show "your plan will cancel at end of period" notice
400
+ }
401
+
402
+ // Use resolved.plan for effective plan ID
403
+ const product = products.find(p => p.id === resolved.plan);
404
+ });
405
+ ```
406
+
363
407
  **⚠️ Auth State Timing**:
364
408
 
365
409
  Methods like `auth.isAuthenticated()`, `auth.getUser()`, and `auth.getIdToken()` read the current state directly — they may return `null` before auth settles.
@@ -159,6 +159,9 @@ class Auth {
159
159
  // Ensure account is always a resolved object
160
160
  state.account = state.account || resolveAccount({}, { uid: user?.uid });
161
161
 
162
+ // Derive resolved subscription state for bindings and consumers
163
+ state.resolved = this.resolveSubscription(state.account);
164
+
162
165
  // Update bindings and storage once per auth state change
163
166
  if (!this._hasProcessedStateChange) {
164
167
  this.manager.bindings().update({ auth: state });
@@ -218,6 +221,37 @@ class Auth {
218
221
  });
219
222
  }
220
223
 
224
+ // Resolves calculated subscription fields that require derivation logic
225
+ // Raw data (product.id, status, trial, cancellation) is on account.subscription directly
226
+ // Returns: { plan, active, trialing, cancelling }
227
+ // - plan: the plan ID the user effectively has access to RIGHT NOW ('basic' if cancelled/suspended)
228
+ // - active: user has active access (active, trialing, or cancelling)
229
+ // - trialing: user is in an active trial (backend status is 'active' but trial hasn't expired)
230
+ // - cancelling: cancellation is pending (backend status is 'active' but cancellation.pending is true)
231
+ resolveSubscription(account) {
232
+ const subscription = (account || this.manager.storage().get('auth', {})?.account)?.subscription || {};
233
+ const productId = subscription.product?.id || 'basic';
234
+
235
+ // Derive trial and cancelling states from raw backend data
236
+ let trialing = false;
237
+ let cancelling = false;
238
+
239
+ if (productId !== 'basic' && subscription.status === 'active') {
240
+ trialing = !!(subscription.trial?.claimed
241
+ && subscription.trial?.expires?.timestampUNIX > Math.floor(Date.now() / 1000));
242
+ cancelling = !trialing && !!subscription.cancellation?.pending;
243
+ }
244
+
245
+ const active = (productId !== 'basic' && subscription.status === 'active');
246
+
247
+ return {
248
+ plan: active ? productId : 'basic',
249
+ active,
250
+ trialing,
251
+ cancelling,
252
+ };
253
+ }
254
+
221
255
  // Get ID token for the current user
222
256
  async getIdToken(forceRefresh = false) {
223
257
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "web-manager",
3
- "version": "4.1.23",
3
+ "version": "4.1.25",
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",