web-manager 3.2.75 → 4.0.1

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.
@@ -0,0 +1,329 @@
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
+ // 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
+
30
+ // Register service worker
31
+ async register(options = {}) {
32
+ try {
33
+ if (!this.isSupported()) {
34
+ console.warn('Service Workers are not supported');
35
+ return null;
36
+ }
37
+
38
+ const swPath = options.path || this.manager.config.serviceWorker?.config?.path || '/service-worker.js';
39
+ const scope = options.scope || '/';
40
+
41
+ // Build config object to pass to service worker
42
+ const config = {
43
+ app: this.manager.config.brand?.id,
44
+ environment: this.manager.config.environment,
45
+ buildTime: this.manager.config.buildTime,
46
+ firebase: this.manager.config.firebase?.app?.config || null
47
+ };
48
+
49
+ // Register and handle everything
50
+ const registration = await navigator.serviceWorker.register(swPath, {
51
+ scope,
52
+ updateViaCache: 'none' // Always check server for updates
53
+ });
54
+
55
+ this._registration = registration;
56
+ this.manager.state.serviceWorker = registration;
57
+
58
+ // Set up update handlers
59
+ this._setupUpdateHandlers(registration);
60
+
61
+ // Wait for service worker to be ready and send config
62
+ await navigator.serviceWorker.ready;
63
+
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
+
74
+ this._setupMessageChannel();
75
+ }
76
+
77
+ // Check for updates (this will detect if service worker file changed)
78
+ if (options.checkForUpdate !== false) {
79
+ registration.update();
80
+ }
81
+
82
+ return registration;
83
+ } catch (error) {
84
+ console.error('Service Worker registration failed:', error);
85
+ throw error;
86
+ }
87
+ }
88
+
89
+ // Unregister service worker
90
+ async unregister() {
91
+ try {
92
+ if (!this._registration) {
93
+ const registrations = await navigator.serviceWorker.getRegistrations();
94
+ for (const registration of registrations) {
95
+ await registration.unregister();
96
+ }
97
+ } else {
98
+ await this._registration.unregister();
99
+ }
100
+
101
+ this._registration = null;
102
+ this.manager.state.serviceWorker = null;
103
+
104
+ return true;
105
+ } catch (error) {
106
+ console.error('Service Worker unregistration failed:', error);
107
+ return false;
108
+ }
109
+ }
110
+
111
+ // Get current registration
112
+ getRegistration() {
113
+ return this._registration;
114
+ }
115
+
116
+ // Check for updates
117
+ async update() {
118
+ try {
119
+ if (!this._registration) {
120
+ throw new Error('No service worker registered');
121
+ }
122
+
123
+ await this._registration.update();
124
+ return true;
125
+ } catch (error) {
126
+ console.error('Service Worker update failed:', error);
127
+ return false;
128
+ }
129
+ }
130
+
131
+ // Post message to service worker
132
+ postMessage(message, options = {}) {
133
+ return new Promise((resolve, reject) => {
134
+ if (!this.isSupported()) {
135
+ return reject(new Error('Service Workers not supported'));
136
+ }
137
+
138
+ const controller = this._registration?.active || navigator.serviceWorker.controller;
139
+
140
+ if (!controller) {
141
+ return reject(new Error('No active service worker'));
142
+ }
143
+
144
+ const messageChannel = new MessageChannel();
145
+ const timeout = options.timeout || 5000;
146
+ let timeoutId;
147
+
148
+ // Set up timeout
149
+ if (timeout > 0) {
150
+ timeoutId = setTimeout(() => {
151
+ messageChannel.port1.close();
152
+ reject(new Error('Service worker message timeout'));
153
+ }, timeout);
154
+ }
155
+
156
+ // Listen for response
157
+ messageChannel.port1.onmessage = (event) => {
158
+ clearTimeout(timeoutId);
159
+
160
+ if (event.data.error) {
161
+ reject(new Error(event.data.error));
162
+ } else {
163
+ resolve(event.data);
164
+ }
165
+ };
166
+
167
+ // Send message
168
+ controller.postMessage(message, [messageChannel.port2]);
169
+ });
170
+ }
171
+
172
+ // Listen for messages from service worker
173
+ onMessage(type, handler) {
174
+ if (!this.isSupported()) {
175
+ return () => {};
176
+ }
177
+
178
+ // Store handler
179
+ if (!this._messageHandlers.has(type)) {
180
+ this._messageHandlers.set(type, new Set());
181
+ }
182
+ this._messageHandlers.get(type).add(handler);
183
+
184
+ // Set up global message listener if not already done
185
+ if (this._messageHandlers.size === 1) {
186
+ navigator.serviceWorker.addEventListener('message', this._handleMessage.bind(this));
187
+ }
188
+
189
+ // Return unsubscribe function
190
+ return () => {
191
+ const handlers = this._messageHandlers.get(type);
192
+ if (handlers) {
193
+ handlers.delete(handler);
194
+ if (handlers.size === 0) {
195
+ this._messageHandlers.delete(type);
196
+ }
197
+ }
198
+ };
199
+ }
200
+
201
+ // Skip waiting and activate new service worker
202
+ async skipWaiting() {
203
+ try {
204
+ if (!this._registration?.waiting) {
205
+ throw new Error('No service worker waiting');
206
+ }
207
+
208
+ // Post message to skip waiting
209
+ await this.postMessage({ action: 'skipWaiting' });
210
+
211
+ // Reload page after activation
212
+ let refreshing = false;
213
+ navigator.serviceWorker.addEventListener('controllerchange', () => {
214
+ if (!refreshing) {
215
+ refreshing = true;
216
+ window.location.reload();
217
+ }
218
+ });
219
+
220
+ return true;
221
+ } catch (error) {
222
+ console.error('Skip waiting failed:', error);
223
+ return false;
224
+ }
225
+ }
226
+
227
+ // Listen for update events
228
+ onUpdateFound(callback) {
229
+ this._updateCallbacks.push(callback);
230
+
231
+ return () => {
232
+ const index = this._updateCallbacks.indexOf(callback);
233
+ if (index > -1) {
234
+ this._updateCallbacks.splice(index, 1);
235
+ }
236
+ };
237
+ }
238
+
239
+ // Get service worker state
240
+ getState() {
241
+ if (!this._registration) {
242
+ return 'none';
243
+ }
244
+
245
+ if (this._registration.installing) {
246
+ return 'installing';
247
+ } else if (this._registration.waiting) {
248
+ return 'waiting';
249
+ } else if (this._registration.active) {
250
+ return 'active';
251
+ }
252
+
253
+ return 'unknown';
254
+ }
255
+
256
+ // Private: Set up update handlers
257
+ _setupUpdateHandlers(registration) {
258
+ // Listen for updates
259
+ registration.addEventListener('updatefound', () => {
260
+ const newWorker = registration.installing;
261
+
262
+ if (!newWorker) return;
263
+
264
+ newWorker.addEventListener('statechange', () => {
265
+ if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
266
+ // New service worker available
267
+ this._notifyUpdateCallbacks({
268
+ type: 'update-available',
269
+ worker: newWorker
270
+ });
271
+
272
+ // Automatically skip waiting and activate new worker
273
+ if (this.manager.config.serviceWorker?.autoUpdate !== false) {
274
+ this.skipWaiting();
275
+ }
276
+ }
277
+ });
278
+ });
279
+
280
+ // Listen for controller changes
281
+ let refreshing = false;
282
+ navigator.serviceWorker.addEventListener('controllerchange', () => {
283
+ if (!refreshing) {
284
+ this._notifyUpdateCallbacks({
285
+ type: 'controller-change'
286
+ });
287
+ }
288
+ });
289
+ }
290
+
291
+ // Private: Notify update callbacks
292
+ _notifyUpdateCallbacks(event) {
293
+ this._updateCallbacks.forEach(callback => {
294
+ try {
295
+ callback(event);
296
+ } catch (error) {
297
+ console.error('Update callback error:', error);
298
+ }
299
+ });
300
+ }
301
+
302
+ // Private: Handle incoming messages
303
+ _handleMessage(event) {
304
+ const { type, ...data } = event.data || {};
305
+
306
+ if (!type) return;
307
+
308
+ const handlers = this._messageHandlers.get(type);
309
+ if (handlers) {
310
+ handlers.forEach(handler => {
311
+ try {
312
+ handler(data, event);
313
+ } catch (error) {
314
+ console.error('Message handler error:', error);
315
+ }
316
+ });
317
+ }
318
+ }
319
+
320
+ // Private: Set up message channel
321
+ _setupMessageChannel() {
322
+ // This ensures we can communicate with the service worker
323
+ navigator.serviceWorker.ready.then(() => {
324
+ console.log('Service Worker ready for messaging');
325
+ });
326
+ }
327
+ }
328
+
329
+ export default ServiceWorker;
@@ -0,0 +1,132 @@
1
+ import { get as _get, set as _set } from 'lodash';
2
+
3
+ class Storage {
4
+ constructor() {
5
+ this.storageKey = '_manager';
6
+ this.pseudoStorage = {};
7
+ }
8
+
9
+ get(path, defaultValue) {
10
+ let usableStorage;
11
+
12
+ // Try to parse the localStorage object
13
+ try {
14
+ usableStorage = JSON.parse(window.localStorage.getItem(this.storageKey) || '{}');
15
+ } catch (e) {
16
+ usableStorage = this.pseudoStorage;
17
+ }
18
+
19
+ // If there's no path, return the entire storage object
20
+ if (!path) {
21
+ return usableStorage || defaultValue;
22
+ }
23
+
24
+ // Return the value at the path
25
+ return _get(usableStorage, path, defaultValue);
26
+ }
27
+
28
+ set(path, value) {
29
+ let usableStorage;
30
+
31
+ // Try to get the current storage
32
+ try {
33
+ usableStorage = this.get();
34
+ } catch (e) {
35
+ usableStorage = this.pseudoStorage;
36
+ }
37
+
38
+ // If there's no path, replace the entire storage object
39
+ if (!path) {
40
+ usableStorage = value || {};
41
+ } else {
42
+ // Set the value at the path
43
+ _set(usableStorage, path, value);
44
+ }
45
+
46
+ // Try to set the localStorage object
47
+ try {
48
+ window.localStorage.setItem(this.storageKey, JSON.stringify(usableStorage));
49
+ } catch (e) {
50
+ this.pseudoStorage = usableStorage;
51
+ }
52
+
53
+ return usableStorage;
54
+ }
55
+
56
+ remove(path) {
57
+ if (!path) {
58
+ this.clear();
59
+ } else {
60
+ this.set(path, undefined);
61
+ }
62
+ }
63
+
64
+ clear() {
65
+ try {
66
+ window.localStorage.setItem(this.storageKey, '{}');
67
+ } catch (e) {
68
+ this.pseudoStorage = {};
69
+ }
70
+ }
71
+
72
+ // Session storage methods
73
+ session = {
74
+ get: (path, defaultValue) => {
75
+ let usableStorage;
76
+
77
+ try {
78
+ usableStorage = JSON.parse(window.sessionStorage.getItem(this.storageKey) || '{}');
79
+ } catch (e) {
80
+ return defaultValue;
81
+ }
82
+
83
+ if (!path) {
84
+ return usableStorage || defaultValue;
85
+ }
86
+
87
+ return _get(usableStorage, path, defaultValue);
88
+ },
89
+
90
+ set: (path, value) => {
91
+ let usableStorage;
92
+
93
+ try {
94
+ usableStorage = this.session.get();
95
+ } catch (e) {
96
+ usableStorage = {};
97
+ }
98
+
99
+ if (!path) {
100
+ usableStorage = value || {};
101
+ } else {
102
+ _set(usableStorage, path, value);
103
+ }
104
+
105
+ try {
106
+ window.sessionStorage.setItem(this.storageKey, JSON.stringify(usableStorage));
107
+ } catch (e) {
108
+ // Silent fail
109
+ }
110
+
111
+ return usableStorage;
112
+ },
113
+
114
+ remove: (path) => {
115
+ if (!path) {
116
+ this.session.clear();
117
+ } else {
118
+ this.session.set(path, undefined);
119
+ }
120
+ },
121
+
122
+ clear: () => {
123
+ try {
124
+ window.sessionStorage.setItem(this.storageKey, '{}');
125
+ } catch (e) {
126
+ // Silent fail
127
+ }
128
+ }
129
+ };
130
+ }
131
+
132
+ export default Storage;
@@ -0,0 +1,143 @@
1
+ // Copy text to clipboard
2
+ export function clipboardCopy(input) {
3
+ // Get the text from the input
4
+ const text = input && input.nodeType
5
+ ? input.value || input.innerText || input.innerHTML
6
+ : input;
7
+
8
+ // Try to use the modern clipboard API
9
+ if (navigator.clipboard && navigator.clipboard.writeText) {
10
+ return navigator.clipboard.writeText(text).catch(() => {
11
+ fallbackCopy(text);
12
+ });
13
+ } else {
14
+ fallbackCopy(text);
15
+ }
16
+
17
+ function fallbackCopy(text) {
18
+ const el = document.createElement('textarea');
19
+ el.setAttribute('style', 'width:1px;border:0;opacity:0;');
20
+ el.value = text;
21
+ document.body.appendChild(el);
22
+ el.select();
23
+
24
+ try {
25
+ document.execCommand('copy');
26
+ } catch (e) {
27
+ console.error('Failed to copy to clipboard');
28
+ }
29
+
30
+ document.body.removeChild(el);
31
+ }
32
+ }
33
+
34
+ // Escape HTML to prevent XSS
35
+ let shadowElement;
36
+ export function escapeHTML(str) {
37
+ if (typeof str !== 'string') {
38
+ return '';
39
+ }
40
+
41
+ shadowElement = shadowElement || document.createElement('p');
42
+ shadowElement.innerHTML = '';
43
+
44
+ // This automatically escapes HTML entities like <, >, &, etc.
45
+ shadowElement.appendChild(document.createTextNode(str));
46
+
47
+ // This is needed to escape quotes to prevent attribute injection
48
+ return shadowElement.innerHTML.replace(/["']/g, (m) => {
49
+ switch (m) {
50
+ case '"':
51
+ return '&quot;';
52
+ default:
53
+ return '&#039;';
54
+ }
55
+ });
56
+ }
57
+
58
+ // Show notification
59
+ export function showNotification(message, options = {}) {
60
+ // Handle different input types
61
+ let text = message;
62
+ let type = options.type || 'info';
63
+
64
+ // If message is an Error object, extract message and default to danger
65
+ if (message instanceof Error) {
66
+ text = message.message;
67
+ type = options.type || 'danger';
68
+ }
69
+
70
+ // Handle string as second parameter for backwards compatibility
71
+ if (typeof options === 'string') {
72
+ options = { type: options };
73
+ type = options.type;
74
+ }
75
+
76
+ // Extract options
77
+ const timeout = options.timeout !== undefined ? options.timeout : 5000;
78
+
79
+ const $notification = document.createElement('div');
80
+ $notification.className = `alert alert-${type} alert-dismissible fade show position-fixed top-0 start-50 translate-middle-x mt-5`;
81
+ $notification.style.zIndex = '9999';
82
+ $notification.innerHTML = `
83
+ ${text}
84
+ <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
85
+ `;
86
+
87
+ document.body.appendChild($notification);
88
+
89
+ // Auto-remove after timeout (unless timeout is 0)
90
+ if (timeout > 0) {
91
+ setTimeout(() => {
92
+ $notification.remove();
93
+ }, timeout);
94
+ }
95
+ }
96
+
97
+ // Get context information
98
+ export function getContext() {
99
+ // Check if mobile
100
+ function isMobile() {
101
+ try {
102
+ // Try modern API first
103
+ const m = navigator.userAgentData?.mobile;
104
+ if (typeof m !== 'undefined') {
105
+ return m === true;
106
+ }
107
+ } catch (e) {
108
+ // Silent fail
109
+ }
110
+
111
+ // Fallback to media query
112
+ try {
113
+ return window.matchMedia('(max-width: 767px)').matches;
114
+ } catch (e) {
115
+ return false;
116
+ }
117
+ }
118
+
119
+ // Return context information
120
+ return {
121
+ client: {
122
+ mobile: isMobile(),
123
+ },
124
+ browser: {
125
+ userAgent: navigator.userAgent,
126
+ language: navigator.language,
127
+ platform: navigator.platform,
128
+ vendor: navigator.vendor,
129
+ },
130
+ screen: {
131
+ width: window.screen?.width,
132
+ height: window.screen?.height,
133
+ availWidth: window.screen?.availWidth,
134
+ availHeight: window.screen?.availHeight,
135
+ colorDepth: window.screen?.colorDepth,
136
+ pixelRatio: window.devicePixelRatio || 1,
137
+ },
138
+ viewport: {
139
+ width: window.innerWidth,
140
+ height: window.innerHeight,
141
+ }
142
+ };
143
+ }
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "web-manager",
3
- "version": "3.2.75",
3
+ "version": "4.0.1",
4
4
  "description": "Easily access important variables such as the query string, current domain, and current page in a single object.",
5
- "main": "index.js",
5
+ "main": "dist/index.js",
6
+ "module": "src/index.js",
6
7
  "scripts": {
7
8
  "test": "./node_modules/mocha/bin/mocha test/ --recursive --timeout=10000",
8
9
  "test_": "npm run prepare && ./node_modules/mocha/bin/mocha test/ --recursive --timeout=10000",
9
10
  "prepare_": "node -e 'require(`prepare-package`)()'",
10
11
  "prepare": "node -e \"require('prepare-package')()\"",
11
- "prepare:watch": "nodemon -w ./src -e '*' --exec 'npm run prepare'"
12
+ "prepare:watch": "node -e \"require('prepare-package/watch')()\""
12
13
  },
13
14
  "engines": {
14
15
  "node": ">=12"
@@ -40,15 +41,14 @@
40
41
  "replace": {}
41
42
  },
42
43
  "dependencies": {
43
- "@sentry/browser": "^8.54.0",
44
- "cookieconsent": "^3.1.1",
45
- "firebase": "^9.23.0",
44
+ "@sentry/browser": "^10.15.0",
45
+ "firebase": "^12.3.0",
46
46
  "itwcw-package-analytics": "^1.0.6",
47
- "lazysizes": "^5.3.2"
47
+ "lodash": "^4.17.21",
48
+ "resolve-account": "^2.0.1"
48
49
  },
49
50
  "devDependencies": {
50
- "lodash": "^4.17.21",
51
51
  "mocha": "^8.4.0",
52
- "prepare-package": "^1.1.13"
52
+ "prepare-package": "^1.2.2"
53
53
  }
54
54
  }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes