web-manager 4.1.37 → 4.1.39

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
@@ -15,6 +15,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
15
15
  - `Security` in case of vulnerabilities.
16
16
 
17
17
  ---
18
+ ## [4.1.39] - 2026-04-10
19
+ ### Changed
20
+ - Converted `Utilities` methods to arrow class fields so `this` is permanently bound to the instance, allowing methods to be destructured, aliased, or passed as callbacks without losing context.
21
+
22
+ ---
23
+ ## [4.1.38] - 2026-04-08
24
+ ### Added
25
+ - Added `sanitizeURL()` utility method that validates URLs against dangerous URI schemes (javascript:, data:, etc.), allowing only http: and https: protocols.
26
+
18
27
  ## [4.1.37] - 2026-04-05
19
28
  ### Added
20
29
  - Added `_resolveUsage()` to auth module that merges account usage data with plan limits from config, exposed as a top-level `usage` binding context.
package/CLAUDE.md CHANGED
@@ -152,7 +152,8 @@ document.body.addEventListener('click', (e) => {
152
152
  ### Auth (`auth.js`)
153
153
  - **Class**: `Auth`
154
154
  - **Key Methods**: `listen(options, callback)`, `isAuthenticated()`, `getUser()`, `signInWithEmailAndPassword()`, `signOut()`, `getIdToken()`, `resolveSubscription(account?)`
155
- - **Bindings**: Updates `auth.user` and `auth.account` context
155
+ - **Bindings**: Updates `auth` and `usage` context on auth settle
156
+ - **Usage Resolution**: `_resolveUsage(state)` merges `account.usage` (Firestore) with plan limits from `config.payment.plans` to produce the `usage` bindings key (e.g., `{ credits: { monthly: 5, limit: 100 } }`)
156
157
 
157
158
  #### resolveSubscription(account?)
158
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.
@@ -268,9 +269,14 @@ Current test coverage is minimal - focuses on configuration and storage.
268
269
 
269
270
  ### Modifying Configuration Defaults
270
271
  1. Edit `_processConfiguration()` in `src/index.js`
271
- 2. Add to `defaults` object
272
+ 2. Add to `defaults` object (e.g., `payment: { processors: {}, plans: [] }`)
272
273
  3. Document in README.md Configuration section
273
274
 
275
+ ### Payment Configuration
276
+ Payment config is set in `_config.yml` under `web_manager.payment` and includes:
277
+ - `processors`: Stripe, PayPal, etc. (publishable keys)
278
+ - `plans`: Array of `{ id, limits: { feature: N } }` used to resolve usage limits on the frontend
279
+
274
280
  ### Adding a Data Binding Action
275
281
  1. Edit `_executeAction()` in `src/modules/bindings.js`
276
282
  2. Add case for new action (e.g., `@class`)
@@ -1,10 +1,16 @@
1
+ // Methods are defined as arrow class fields so `this` is permanently bound to the instance.
2
+ // This means consumers can safely alias or destructure methods without losing context:
3
+ // const { escapeHTML } = webManager.utilities(); // ✓ works
4
+ // const escape = webManager.utilities().escapeHTML; // ✓ works
5
+ // items.map(webManager.utilities().escapeHTML); // ✓ works
6
+ // Safe because webManager.utilities() is a singleton — only one instance ever exists.
1
7
  class Utilities {
2
8
  constructor(manager) {
3
9
  this.manager = manager;
4
10
  }
5
11
 
6
12
  // Copy text to clipboard
7
- clipboardCopy(input) {
13
+ clipboardCopy = (input) => {
8
14
  // Get the text from the input
9
15
  const text = input && input.nodeType
10
16
  ? input.value || input.innerText || input.innerHTML
@@ -38,7 +44,7 @@ class Utilities {
38
44
 
39
45
  // Escape HTML to prevent XSS
40
46
  // Accepts a string, object, or array — walks recursively, escaping all string values
41
- escapeHTML(input) {
47
+ escapeHTML = (input) => {
42
48
  // Strings — escape and return
43
49
  if (typeof input === 'string') {
44
50
  this._shadowElement = this._shadowElement || document.createElement('p');
@@ -81,8 +87,28 @@ class Utilities {
81
87
  return input;
82
88
  }
83
89
 
90
+ // Sanitize URL to prevent javascript:, data:, and other dangerous URI schemes
91
+ // Returns the original URL if safe, or '' if rejected
92
+ sanitizeURL = (url) => {
93
+ if (!url || typeof url !== 'string') {
94
+ return '';
95
+ }
96
+
97
+ try {
98
+ const parsed = new URL(url, window.location.origin);
99
+
100
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
101
+ return '';
102
+ }
103
+
104
+ return url;
105
+ } catch (e) {
106
+ return '';
107
+ }
108
+ }
109
+
84
110
  // Show notification
85
- showNotification(message, options = {}) {
111
+ showNotification = (message, options = {}) => {
86
112
  // Handle different input types
87
113
  let text = message;
88
114
  let type = options.type || 'info';
@@ -128,7 +154,7 @@ class Utilities {
128
154
  }
129
155
 
130
156
  // Get platform (OS)
131
- getPlatform() {
157
+ getPlatform = () => {
132
158
  const ua = navigator.userAgent.toLowerCase();
133
159
  const platform = (navigator.userAgentData?.platform || navigator.platform || '').toLowerCase();
134
160
 
@@ -158,7 +184,7 @@ class Utilities {
158
184
  }
159
185
 
160
186
  // Get browser name
161
- getBrowser() {
187
+ getBrowser = () => {
162
188
  const ua = navigator.userAgent;
163
189
 
164
190
  // Order matters - check more specific browsers first
@@ -197,7 +223,7 @@ class Utilities {
197
223
  }
198
224
 
199
225
  // Get runtime environment
200
- getRuntime() {
226
+ getRuntime = () => {
201
227
  // Use config runtime if provided
202
228
  if (this.manager?.config?.runtime) {
203
229
  return this.manager.config.runtime;
@@ -217,7 +243,7 @@ class Utilities {
217
243
  }
218
244
 
219
245
  // Check if mobile device
220
- isMobile() {
246
+ isMobile = () => {
221
247
  try {
222
248
  // Try modern API first
223
249
  const m = navigator.userAgentData?.mobile;
@@ -237,7 +263,7 @@ class Utilities {
237
263
  }
238
264
 
239
265
  // Get device based on screen width
240
- getDevice() {
266
+ getDevice = () => {
241
267
  const width = window.innerWidth;
242
268
 
243
269
  // Mobile: < 768px (Bootstrap's md breakpoint)
@@ -255,7 +281,7 @@ class Utilities {
255
281
  }
256
282
 
257
283
  // Get context information
258
- getContext() {
284
+ getContext = () => {
259
285
  // Return context information
260
286
  return {
261
287
  client: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "web-manager",
3
- "version": "4.1.37",
3
+ "version": "4.1.39",
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",