web-manager 4.0.0 → 4.0.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
@@ -15,6 +15,10 @@ 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.0.0] - 2025-09-11
19
+ ### BREAKING
20
+ - Updated to ITW 3.0 standard.
21
+
18
22
  ## [3.2.74] - 2025-07-17
19
23
  ### Added
20
24
  - Now looks for `build.json` in the `/@output/build/` directory to ensure it works with Vite's output structure.
package/README.md CHANGED
@@ -270,10 +270,10 @@ Web Manager includes a powerful data binding system that automatically updates y
270
270
  #### Basic Text Binding
271
271
  ```html
272
272
  <!-- Display user email -->
273
- <span data-wm-bind="user.email"></span>
273
+ <span data-wm-bind="auth.user.email"></span>
274
274
 
275
275
  <!-- Display nested properties -->
276
- <div data-wm-bind="account.subscription.plan"></div>
276
+ <div data-wm-bind="auth.account.subscription.plan"></div>
277
277
 
278
278
  <!-- Works with inputs too -->
279
279
  <input data-wm-bind="settings.theme" />
@@ -282,14 +282,14 @@ Web Manager includes a powerful data binding system that automatically updates y
282
282
  #### Conditional Visibility
283
283
  ```html
284
284
  <!-- Show element when condition is true -->
285
- <div data-wm-bind="@show user">Welcome!</div>
286
- <div data-wm-bind="@show user.emailVerified">Email is verified</div>
285
+ <div data-wm-bind="@show auth.user">Welcome!</div>
286
+ <div data-wm-bind="@show auth.user.emailVerified">Email is verified</div>
287
287
 
288
288
  <!-- Hide element when condition is true -->
289
- <div data-wm-bind="@hide user">Please log in</div>
289
+ <div data-wm-bind="@hide auth.user">Please log in</div>
290
290
 
291
291
  <!-- Comparisons -->
292
- <div data-wm-bind="@show subscription.plan === 'premium'">Premium features</div>
292
+ <div data-wm-bind="@show auth.account.subscription.plan === 'premium'">Premium features</div>
293
293
  <div data-wm-bind="@hide settings.notifications === false">Notifications enabled</div>
294
294
  ```
295
295
 
@@ -297,7 +297,7 @@ Web Manager includes a powerful data binding system that automatically updates y
297
297
  ```javascript
298
298
  // Auth data is automatically bound when using auth().listen()
299
299
  Manager.auth().listen({ account: true }, (result) => {
300
- // user and account data are automatically bound to the DOM
300
+ // auth.user and auth.account data are automatically bound to the DOM
301
301
  });
302
302
 
303
303
  // Update bindings with custom data
@@ -0,0 +1,347 @@
1
+ class ServiceWorker {
2
+ constructor(manager) {
3
+ this.manager = manager;
4
+ this._registration = null;
5
+ this._updateCallbacks = [];
6
+ this._messageHandlers = new Map();
7
+ }
8
+
9
+ // Check if service workers are supported
10
+ isSupported() {
11
+ return 'serviceWorker' in navigator;
12
+ }
13
+
14
+ // Register service worker
15
+ async register(options = {}) {
16
+ try {
17
+ if (!this.isSupported()) {
18
+ console.warn('Service Workers are not supported');
19
+ return null;
20
+ }
21
+
22
+ const swPath = options.path || this.manager.config.serviceWorker?.config?.path || '/service-worker.js';
23
+ const scope = options.scope || '/';
24
+
25
+ // Build config object to pass to service worker
26
+ const config = {
27
+ app: this.manager.config.brand?.id,
28
+ environment: this.manager.config.environment,
29
+ buildTime: this.manager.config.buildTime,
30
+ firebase: this.manager.config.firebase?.app?.config || null
31
+ };
32
+
33
+ // Get service worker URL with just cache breaker
34
+ const cacheBreaker = config.buildTime || Date.now();
35
+ const swUrl = `${swPath}?cb=${cacheBreaker}`;
36
+
37
+ // Get existing registrations
38
+ const registrations = await navigator.serviceWorker.getRegistrations();
39
+
40
+ // Check if service worker is already registered for this scope
41
+ let registration = registrations.find(reg =>
42
+ reg.scope === new URL(scope, window.location.href).href
43
+ );
44
+
45
+ // Always register/re-register to ensure we have the correct URL with config
46
+ console.log('----WM SW 1');
47
+
48
+ if (registration) {
49
+ console.log('----WM SW 2');
50
+ console.log('Unregistering existing service worker to update config');
51
+ await registration.unregister();
52
+ // Wait a bit for unregistration to complete
53
+ await new Promise(resolve => setTimeout(resolve, 100));
54
+ }
55
+ console.log('----WM SW 3');
56
+
57
+ console.log('Registering service worker with cache breaker');
58
+ registration = await navigator.serviceWorker.register(swUrl, {
59
+ scope,
60
+ updateViaCache: options.updateViaCache || 'imports'
61
+ });
62
+
63
+ // Wait for the service worker to be ready
64
+ await navigator.serviceWorker.ready;
65
+
66
+ // Send the full config via postMessage after registration
67
+ console.log('----WM SW 4');
68
+ if (registration.active) {
69
+ console.log('----WM SW 5');
70
+ try {
71
+ this.postMessage({
72
+ command: 'update-config',
73
+ payload: config
74
+ });
75
+ } catch (error) {
76
+ console.warn('Could not send config to service worker:', error);
77
+ }
78
+ }
79
+
80
+ this._registration = registration;
81
+ this.manager.state.serviceWorker = registration;
82
+ console.log('----WM SW 6');
83
+
84
+ // Set up update handlers
85
+ this._setupUpdateHandlers(registration);
86
+ console.log('----WM SW 7');
87
+
88
+ // Check for updates
89
+ if (options.checkForUpdate !== false) {
90
+ registration.update();
91
+ }
92
+ console.log('----WM SW 8');
93
+
94
+ // Set up message channel
95
+ if (registration.active) {
96
+ this._setupMessageChannel();
97
+ }
98
+ console.log('----WM SW 9');
99
+
100
+ return registration;
101
+ } catch (error) {
102
+ console.error('Service Worker registration failed:', error);
103
+ throw error;
104
+ }
105
+ }
106
+
107
+ // Unregister service worker
108
+ async unregister() {
109
+ try {
110
+ if (!this._registration) {
111
+ const registrations = await navigator.serviceWorker.getRegistrations();
112
+ for (const registration of registrations) {
113
+ await registration.unregister();
114
+ }
115
+ } else {
116
+ await this._registration.unregister();
117
+ }
118
+
119
+ this._registration = null;
120
+ this.manager.state.serviceWorker = null;
121
+
122
+ return true;
123
+ } catch (error) {
124
+ console.error('Service Worker unregistration failed:', error);
125
+ return false;
126
+ }
127
+ }
128
+
129
+ // Get current registration
130
+ getRegistration() {
131
+ return this._registration;
132
+ }
133
+
134
+ // Check for updates
135
+ async update() {
136
+ try {
137
+ if (!this._registration) {
138
+ throw new Error('No service worker registered');
139
+ }
140
+
141
+ await this._registration.update();
142
+ return true;
143
+ } catch (error) {
144
+ console.error('Service Worker update failed:', error);
145
+ return false;
146
+ }
147
+ }
148
+
149
+ // Post message to service worker
150
+ postMessage(message, options = {}) {
151
+ return new Promise((resolve, reject) => {
152
+ if (!this.isSupported()) {
153
+ return reject(new Error('Service Workers not supported'));
154
+ }
155
+
156
+ const controller = this._registration?.active || navigator.serviceWorker.controller;
157
+
158
+ if (!controller) {
159
+ return reject(new Error('No active service worker'));
160
+ }
161
+
162
+ const messageChannel = new MessageChannel();
163
+ const timeout = options.timeout || 5000;
164
+ let timeoutId;
165
+
166
+ // Set up timeout
167
+ if (timeout > 0) {
168
+ timeoutId = setTimeout(() => {
169
+ messageChannel.port1.close();
170
+ reject(new Error('Service worker message timeout'));
171
+ }, timeout);
172
+ }
173
+
174
+ // Listen for response
175
+ messageChannel.port1.onmessage = (event) => {
176
+ clearTimeout(timeoutId);
177
+
178
+ if (event.data.error) {
179
+ reject(new Error(event.data.error));
180
+ } else {
181
+ resolve(event.data);
182
+ }
183
+ };
184
+
185
+ // Send message
186
+ controller.postMessage(message, [messageChannel.port2]);
187
+ });
188
+ }
189
+
190
+ // Listen for messages from service worker
191
+ onMessage(type, handler) {
192
+ if (!this.isSupported()) {
193
+ return () => {};
194
+ }
195
+
196
+ // Store handler
197
+ if (!this._messageHandlers.has(type)) {
198
+ this._messageHandlers.set(type, new Set());
199
+ }
200
+ this._messageHandlers.get(type).add(handler);
201
+
202
+ // Set up global message listener if not already done
203
+ if (this._messageHandlers.size === 1) {
204
+ navigator.serviceWorker.addEventListener('message', this._handleMessage.bind(this));
205
+ }
206
+
207
+ // Return unsubscribe function
208
+ return () => {
209
+ const handlers = this._messageHandlers.get(type);
210
+ if (handlers) {
211
+ handlers.delete(handler);
212
+ if (handlers.size === 0) {
213
+ this._messageHandlers.delete(type);
214
+ }
215
+ }
216
+ };
217
+ }
218
+
219
+ // Skip waiting and activate new service worker
220
+ async skipWaiting() {
221
+ try {
222
+ if (!this._registration?.waiting) {
223
+ throw new Error('No service worker waiting');
224
+ }
225
+
226
+ // Post message to skip waiting
227
+ await this.postMessage({ action: 'skipWaiting' });
228
+
229
+ // Reload page after activation
230
+ let refreshing = false;
231
+ navigator.serviceWorker.addEventListener('controllerchange', () => {
232
+ if (!refreshing) {
233
+ refreshing = true;
234
+ window.location.reload();
235
+ }
236
+ });
237
+
238
+ return true;
239
+ } catch (error) {
240
+ console.error('Skip waiting failed:', error);
241
+ return false;
242
+ }
243
+ }
244
+
245
+ // Listen for update events
246
+ onUpdateFound(callback) {
247
+ this._updateCallbacks.push(callback);
248
+
249
+ return () => {
250
+ const index = this._updateCallbacks.indexOf(callback);
251
+ if (index > -1) {
252
+ this._updateCallbacks.splice(index, 1);
253
+ }
254
+ };
255
+ }
256
+
257
+ // Get service worker state
258
+ getState() {
259
+ if (!this._registration) {
260
+ return 'none';
261
+ }
262
+
263
+ if (this._registration.installing) {
264
+ return 'installing';
265
+ } else if (this._registration.waiting) {
266
+ return 'waiting';
267
+ } else if (this._registration.active) {
268
+ return 'active';
269
+ }
270
+
271
+ return 'unknown';
272
+ }
273
+
274
+ // Private: Set up update handlers
275
+ _setupUpdateHandlers(registration) {
276
+ // Listen for updates
277
+ registration.addEventListener('updatefound', () => {
278
+ const newWorker = registration.installing;
279
+
280
+ if (!newWorker) return;
281
+
282
+ newWorker.addEventListener('statechange', () => {
283
+ if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
284
+ // New service worker available
285
+ this._notifyUpdateCallbacks({
286
+ type: 'update-available',
287
+ worker: newWorker
288
+ });
289
+
290
+ // Automatically skip waiting and activate new worker
291
+ if (this.manager.config.serviceWorker?.autoUpdate !== false) {
292
+ this.skipWaiting();
293
+ }
294
+ }
295
+ });
296
+ });
297
+
298
+ // Listen for controller changes
299
+ let refreshing = false;
300
+ navigator.serviceWorker.addEventListener('controllerchange', () => {
301
+ if (!refreshing) {
302
+ this._notifyUpdateCallbacks({
303
+ type: 'controller-change'
304
+ });
305
+ }
306
+ });
307
+ }
308
+
309
+ // Private: Notify update callbacks
310
+ _notifyUpdateCallbacks(event) {
311
+ this._updateCallbacks.forEach(callback => {
312
+ try {
313
+ callback(event);
314
+ } catch (error) {
315
+ console.error('Update callback error:', error);
316
+ }
317
+ });
318
+ }
319
+
320
+ // Private: Handle incoming messages
321
+ _handleMessage(event) {
322
+ const { type, ...data } = event.data || {};
323
+
324
+ if (!type) return;
325
+
326
+ const handlers = this._messageHandlers.get(type);
327
+ if (handlers) {
328
+ handlers.forEach(handler => {
329
+ try {
330
+ handler(data, event);
331
+ } catch (error) {
332
+ console.error('Message handler error:', error);
333
+ }
334
+ });
335
+ }
336
+ }
337
+
338
+ // Private: Set up message channel
339
+ _setupMessageChannel() {
340
+ // This ensures we can communicate with the service worker
341
+ navigator.serviceWorker.ready.then(() => {
342
+ console.log('Service Worker ready for messaging');
343
+ });
344
+ }
345
+ }
346
+
347
+ export default ServiceWorker;
package/dist/index.js CHANGED
@@ -86,7 +86,7 @@ class Manager {
86
86
 
87
87
  // Initialize service worker if enabled
88
88
  if (this.config.serviceWorker?.enabled) {
89
- await this._serviceWorker.register({
89
+ this._serviceWorker.register({
90
90
  path: this.config.serviceWorker?.config?.path
91
91
  });
92
92
  }
@@ -1,8 +1,11 @@
1
+ import resolveAccount from 'resolve-account';
2
+
1
3
  class Auth {
2
4
  constructor(manager) {
3
5
  this.manager = manager;
4
6
  this._authStateCallbacks = [];
5
7
  this._readyCallbacks = [];
8
+ this._hasProcessedStateChange = false;
6
9
  }
7
10
 
8
11
  // Check if user is authenticated
@@ -67,8 +70,14 @@ class Auth {
67
70
 
68
71
  // If Firebase is not enabled, call callback immediately with null
69
72
  if (!this.manager.config.firebase?.app?.enabled) {
70
- callback({ user: null, account: null });
71
- return () => {}; // Return empty unsubscribe function
73
+ // Call callback with null user and empty account
74
+ callback({
75
+ user: null,
76
+ account: resolveAccount({}, {})
77
+ });
78
+
79
+ // Return empty unsubscribe function
80
+ return () => {};
72
81
  }
73
82
 
74
83
  // Function to get current state and call callback
@@ -77,18 +86,33 @@ class Auth {
77
86
  const state = { user: this.getUser() };
78
87
 
79
88
  // Then, add account data if requested and user exists
80
- if (options.account && user && this.manager.firebaseFirestore) {
89
+ // if (options.account && user && this.manager.firebaseFirestore) {
90
+ // Fetch account if the user is logged in AND Firestore is available
91
+ if (user && this.manager.firebaseFirestore) {
81
92
  try {
82
93
  state.account = await this._getAccountData(user.uid);
83
94
  } catch (error) {
84
- state.account = null;
95
+ // Pass error to Sentry
96
+ this.manager.sentry().captureException(new Error('Failed to get account data', { cause: error }));
85
97
  }
86
- } else {
87
- state.account = null;
88
98
  }
89
99
 
90
- // Update bindings with auth data
91
- this.manager.bindings().update(state);
100
+ // Always ensure account is at least a default resolved object
101
+ state.account = state.account || resolveAccount({}, { uid: user?.uid });
102
+
103
+ // Process state change (update bindings and storage) only once across all callbacks
104
+ // Now ONLY the first listener will process the state change until the next auth state change
105
+ if (!this._hasProcessedStateChange) {
106
+ // Run update - nest state under 'auth' key for consistent access
107
+ this.manager.bindings().update({ auth: state });
108
+
109
+ // Save to storage
110
+ const storage = this.manager.storage();
111
+ storage.set('auth', state);
112
+
113
+ // Mark that we've processed this state change
114
+ this._hasProcessedStateChange = true;
115
+ }
92
116
 
93
117
  // Call the provided callback with the state
94
118
  callback(state);
@@ -97,15 +121,27 @@ class Auth {
97
121
  let hasCalledback = false;
98
122
 
99
123
  // Set up listener for auth state changes
100
- return this.onAuthStateChanged((user) => {
124
+ const unsubscribe = this.onAuthStateChanged((user) => {
125
+ // If once option is set, unsubscribe
126
+ // We have to do this here because unsubscribe is only available after this call
127
+ if (options.once && unsubscribe) {
128
+ unsubscribe();
129
+ return;
130
+ }
131
+
101
132
  // Wait for settled state before first callback
102
133
  if (!hasCalledback && !this.manager._firebaseAuthInitialized) {
103
134
  return; // Auth state not yet determined
104
135
  }
105
136
 
137
+ // Mark that we've called back at least once
106
138
  hasCalledback = true;
139
+
140
+ // Get current state and call the callback
107
141
  getStateAndCallback(user);
108
142
  });
143
+
144
+ return unsubscribe;
109
145
  }
110
146
 
111
147
  // Listen for auth state changes
@@ -128,11 +164,8 @@ class Auth {
128
164
 
129
165
  // Internal method to handle auth state changes
130
166
  _handleAuthStateChange(user) {
131
- // Always update bindings when auth state changes (basic update without account)
132
- this.manager.bindings().update({
133
- user: this.getUser(),
134
- account: null
135
- });
167
+ // Reset state processing flag for new auth state
168
+ this._hasProcessedStateChange = false;
136
169
 
137
170
  // Call all registered callbacks
138
171
  this._authStateCallbacks.forEach(callback => {
@@ -173,6 +206,22 @@ class Auth {
173
206
  }
174
207
  }
175
208
 
209
+ // Sign in with email and password
210
+ async signInWithEmailAndPassword(email, password) {
211
+ try {
212
+ if (!this.manager.firebaseAuth) {
213
+ throw new Error('Firebase Auth is not initialized');
214
+ }
215
+
216
+ const { signInWithEmailAndPassword } = await import('firebase/auth');
217
+ const userCredential = await signInWithEmailAndPassword(this.manager.firebaseAuth, email, password);
218
+ return userCredential.user;
219
+ } catch (error) {
220
+ console.error('Sign in with email and password error:', error);
221
+ throw error;
222
+ }
223
+ }
224
+
176
225
  // Sign out the current user
177
226
  async signOut() {
178
227
  try {
@@ -193,7 +242,6 @@ class Auth {
193
242
  }
194
243
 
195
244
  const { doc, getDoc } = await import('firebase/firestore');
196
- const resolveAccount = (await import('resolve-account')).default;
197
245
 
198
246
  const accountDoc = doc(this.manager.firebaseFirestore, 'users', uid);
199
247
  const snapshot = await getDoc(accountDoc);
@@ -81,14 +81,14 @@ class Bindings {
81
81
  const attrName = attrParts[0];
82
82
  const attrExpression = attrParts.slice(1).join(' ');
83
83
  const attrValue = this._resolvePath(context, attrExpression) || '';
84
-
84
+
85
85
  if (attrValue) {
86
86
  element.setAttribute(attrName, attrValue);
87
87
  } else {
88
88
  element.removeAttribute(attrName);
89
89
  }
90
90
  break;
91
-
91
+
92
92
  case '@text':
93
93
  default:
94
94
  // Set text content (default behavior)
@@ -121,7 +121,7 @@ class Bindings {
121
121
  _evaluateCondition(condition, context) {
122
122
  try {
123
123
  // Replace context references with actual values
124
- // Support: user.field, account.field, simple comparisons
124
+ // Support: auth.user.field, auth.account.field, simple comparisons
125
125
 
126
126
  // Check for negation operator at the start
127
127
  if (condition.trim().startsWith('!')) {
@@ -169,7 +169,7 @@ class Bindings {
169
169
  default: return false;
170
170
  }
171
171
  } else {
172
- // Simple truthy check (e.g., "user.emailVerified" or "account")
172
+ // Simple truthy check (e.g., "auth.user.emailVerified" or "auth.account")
173
173
  const value = this._resolvePath(context, condition.trim());
174
174
  return !!value;
175
175
  }
@@ -12,21 +12,16 @@ export function loadScript(options) {
12
12
  defer = false,
13
13
  crossorigin = false,
14
14
  integrity = null,
15
- attributes = [],
15
+ attributes = {},
16
16
  timeout = 60000,
17
- retries = 0
17
+ retries = 0,
18
+ parent = null
18
19
  } = options;
19
20
 
20
21
  if (!src) {
21
22
  return reject(new Error('Script source is required'));
22
23
  }
23
24
 
24
- // Check if script already exists
25
- const existingScript = document.querySelector(`script[src="${src}"]`);
26
- if (existingScript) {
27
- return resolve({ script: existingScript, cached: true });
28
- }
29
-
30
25
  let timeoutId;
31
26
  let retryCount = 0;
32
27
 
@@ -45,10 +40,8 @@ export function loadScript(options) {
45
40
  }
46
41
 
47
42
  // Add custom attributes
48
- attributes.forEach(attr => {
49
- if (attr.name && attr.value !== undefined) {
50
- script.setAttribute(attr.name, attr.value);
51
- }
43
+ Object.keys(attributes).forEach(name => {
44
+ script.setAttribute(name, attributes[name]);
52
45
  });
53
46
 
54
47
  // Set up timeout
@@ -68,11 +61,12 @@ export function loadScript(options) {
68
61
  script.onerror = (error) => {
69
62
  clearTimeout(timeoutId);
70
63
  script.remove();
71
- handleError(new Error(`Failed to load script: ${src}`));
64
+ handleError(new Error(`Failed to load script ${src}`, { cause: error }));
72
65
  };
73
66
 
74
67
  // Append to document
75
- (document.head || document.documentElement).appendChild(script);
68
+ const $targetParent = parent || document.head || document.documentElement;
69
+ $targetParent.appendChild(script);
76
70
  }
77
71
 
78
72
  function handleError(error) {
@@ -11,6 +11,22 @@ class ServiceWorker {
11
11
  return 'serviceWorker' in navigator;
12
12
  }
13
13
 
14
+ // Return promise that resolves when service worker is ready
15
+ async ready() {
16
+ if (!this.isSupported()) {
17
+ throw new Error('Service Workers not supported');
18
+ }
19
+
20
+ // If already registered and active
21
+ if (this._registration?.active) {
22
+ return this._registration;
23
+ }
24
+
25
+ // Wait for service worker to be ready
26
+ const registration = await navigator.serviceWorker.ready;
27
+ return registration;
28
+ }
29
+
14
30
  // Register service worker
15
31
  async register(options = {}) {
16
32
  try {
@@ -30,30 +46,11 @@ class ServiceWorker {
30
46
  firebase: this.manager.config.firebase?.app?.config || null
31
47
  };
32
48
 
33
- // Get service worker URL with config
34
- const swUrl = `${swPath}?config=${encodeURIComponent(JSON.stringify(config))}`;
35
-
36
- // Get existing registrations
37
- const registrations = await navigator.serviceWorker.getRegistrations();
38
-
39
- // Check if service worker is already registered for this scope
40
- let registration = registrations.find(reg =>
41
- reg.scope === new URL(scope, window.location.href).href
42
- );
43
-
44
- // This helps the .register() method NOT HANG FOREVER
45
- if (registration) {
46
- console.log('Using existing service worker registration');
47
- // Check for updates on existing registration
48
- registration.update();
49
- } else {
50
- console.log('Registering new service worker');
51
- // Register with config in URL
52
- registration = await navigator.serviceWorker.register(swUrl, {
53
- scope,
54
- updateViaCache: options.updateViaCache || 'imports'
55
- });
56
- }
49
+ // Register and handle everything
50
+ const registration = await navigator.serviceWorker.register(swPath, {
51
+ scope,
52
+ updateViaCache: 'none' // Always check server for updates
53
+ });
57
54
 
58
55
  this._registration = registration;
59
56
  this.manager.state.serviceWorker = registration;
@@ -61,16 +58,27 @@ class ServiceWorker {
61
58
  // Set up update handlers
62
59
  this._setupUpdateHandlers(registration);
63
60
 
64
- // Check for updates
65
- if (options.checkForUpdate !== false) {
66
- registration.update();
67
- }
68
-
69
- // Set up message channel
61
+ // Wait for service worker to be ready and send config
62
+ await navigator.serviceWorker.ready;
63
+
70
64
  if (registration.active) {
65
+ try {
66
+ this.postMessage({
67
+ command: 'update-config',
68
+ payload: config
69
+ });
70
+ } catch (error) {
71
+ console.warn('Could not send config to service worker:', error);
72
+ }
73
+
71
74
  this._setupMessageChannel();
72
75
  }
73
76
 
77
+ // Check for updates (this will detect if service worker file changed)
78
+ if (options.checkForUpdate !== false) {
79
+ registration.update();
80
+ }
81
+
74
82
  return registration;
75
83
  } catch (error) {
76
84
  console.error('Service Worker registration failed:', error);
@@ -260,7 +268,7 @@ class ServiceWorker {
260
268
  type: 'update-available',
261
269
  worker: newWorker
262
270
  });
263
-
271
+
264
272
  // Automatically skip waiting and activate new worker
265
273
  if (this.manager.config.serviceWorker?.autoUpdate !== false) {
266
274
  this.skipWaiting();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "web-manager",
3
- "version": "4.0.0",
3
+ "version": "4.0.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",
@@ -41,14 +41,13 @@
41
41
  "replace": {}
42
42
  },
43
43
  "dependencies": {
44
- "@sentry/browser": "^10.8.0",
45
- "firebase": "^12.2.1",
44
+ "@sentry/browser": "^10.15.0",
45
+ "firebase": "^12.3.0",
46
46
  "itwcw-package-analytics": "^1.0.6",
47
47
  "lodash": "^4.17.21",
48
- "resolve-account": "^2.0.0"
48
+ "resolve-account": "^2.0.1"
49
49
  },
50
50
  "devDependencies": {
51
- "lodash": "^4.17.21",
52
51
  "mocha": "^8.4.0",
53
52
  "prepare-package": "^1.2.2"
54
53
  }