web-manager 4.3.2 → 4.3.3
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/CLAUDE.md +3 -0
- package/docs/cdp-debugging.md +29 -0
- package/package.json +7 -1
- package/src/index.js +702 -0
- package/src/modules/analytics.js +250 -0
- package/src/modules/auth.js +420 -0
- package/src/modules/bindings.js +314 -0
- package/src/modules/dom.js +96 -0
- package/src/modules/firestore.js +306 -0
- package/src/modules/notifications.js +391 -0
- package/src/modules/sentry.js +199 -0
- package/src/modules/service-worker.js +196 -0
- package/src/modules/storage.js +133 -0
- package/src/modules/usage.js +264 -0
- package/src/modules/utilities.js +310 -0
- package/CHANGELOG.md +0 -242
- package/TODO.md +0 -59
- package/_legacy/helpers/auth-pages.js +0 -24
- package/_legacy/index.js +0 -1854
- package/_legacy/lib/account.js +0 -666
- package/_legacy/lib/debug.js +0 -56
- package/_legacy/lib/dom.js +0 -351
- package/_legacy/lib/require.js +0 -5
- package/_legacy/lib/storage.js +0 -78
- package/_legacy/lib/utilities.js +0 -180
- package/_legacy/service-worker copy.js +0 -347
- package/_legacy/test/test.js +0 -158
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
class ServiceWorker {
|
|
2
|
+
constructor(manager) {
|
|
3
|
+
this.manager = manager;
|
|
4
|
+
this._registration = null;
|
|
5
|
+
this._messageHandlers = new Map();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// Check if service workers are supported
|
|
9
|
+
isSupported() {
|
|
10
|
+
return 'serviceWorker' in navigator;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Return promise that resolves when service worker is ready
|
|
14
|
+
async ready() {
|
|
15
|
+
if (!this.isSupported()) {
|
|
16
|
+
throw new Error('Service Workers not supported');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// If already registered and active
|
|
20
|
+
if (this._registration?.active) {
|
|
21
|
+
return this._registration;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Wait for service worker to be ready
|
|
25
|
+
const registration = await navigator.serviceWorker.ready;
|
|
26
|
+
return registration;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Register service worker
|
|
30
|
+
async register(options = {}) {
|
|
31
|
+
try {
|
|
32
|
+
if (!this.isSupported()) {
|
|
33
|
+
console.warn('Service Workers are not supported');
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const swPath = options.path || this.manager.config.serviceWorker?.config?.path || '/service-worker.js';
|
|
38
|
+
const scope = options.scope || '/';
|
|
39
|
+
|
|
40
|
+
// Build config object to pass to service worker
|
|
41
|
+
const config = {
|
|
42
|
+
brand: this.manager.config.brand?.id,
|
|
43
|
+
environment: this.manager.config.environment,
|
|
44
|
+
buildTime: this.manager.config.buildTime,
|
|
45
|
+
firebase: this.manager._resolveFirebaseConfig()
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Register service worker
|
|
49
|
+
const registration = await navigator.serviceWorker.register(swPath, {
|
|
50
|
+
scope,
|
|
51
|
+
updateViaCache: 'none'
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Store registration
|
|
55
|
+
this._registration = registration;
|
|
56
|
+
this.manager.state.serviceWorker = registration;
|
|
57
|
+
|
|
58
|
+
// Wait for service worker to be ready and send config
|
|
59
|
+
await navigator.serviceWorker.ready;
|
|
60
|
+
|
|
61
|
+
// Send config to active service worker
|
|
62
|
+
// Removed due to issues init'ing firebase asynchronously in SW (now config is fetched directly in SW)
|
|
63
|
+
// if (registration.active) {
|
|
64
|
+
// try {
|
|
65
|
+
// this.postMessage({
|
|
66
|
+
// command: 'update-config',
|
|
67
|
+
// payload: config
|
|
68
|
+
// });
|
|
69
|
+
// } catch (error) {
|
|
70
|
+
// console.warn('Could not send config to service worker:', error);
|
|
71
|
+
// }
|
|
72
|
+
// }
|
|
73
|
+
|
|
74
|
+
// Resolve with registration
|
|
75
|
+
return registration;
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error('Service Worker registration failed:', error);
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Get current registration
|
|
83
|
+
getRegistration() {
|
|
84
|
+
return this._registration;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Post message to service worker
|
|
88
|
+
postMessage(message, options = {}) {
|
|
89
|
+
return new Promise((resolve, reject) => {
|
|
90
|
+
// Check support
|
|
91
|
+
if (!this.isSupported()) {
|
|
92
|
+
return reject(new Error('Service Workers not supported'));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Get active service worker
|
|
96
|
+
const controller = this._registration?.active || navigator.serviceWorker.controller;
|
|
97
|
+
|
|
98
|
+
if (!controller) {
|
|
99
|
+
return reject(new Error('No active service worker'));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Create message channel for two-way communication
|
|
103
|
+
const messageChannel = new MessageChannel();
|
|
104
|
+
const timeout = options.timeout || 5000;
|
|
105
|
+
let timeoutId;
|
|
106
|
+
|
|
107
|
+
// Set up timeout to prevent hanging
|
|
108
|
+
if (timeout > 0) {
|
|
109
|
+
timeoutId = setTimeout(() => {
|
|
110
|
+
messageChannel.port1.close();
|
|
111
|
+
reject(new Error('Service worker message timeout'));
|
|
112
|
+
}, timeout);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Listen for response from service worker
|
|
116
|
+
messageChannel.port1.onmessage = (event) => {
|
|
117
|
+
clearTimeout(timeoutId);
|
|
118
|
+
|
|
119
|
+
if (event.data.error) {
|
|
120
|
+
reject(new Error(event.data.error));
|
|
121
|
+
} else {
|
|
122
|
+
resolve(event.data);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// Send message with port for reply
|
|
127
|
+
controller.postMessage(message, [messageChannel.port2]);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Listen for messages from service worker
|
|
132
|
+
onMessage(type, handler) {
|
|
133
|
+
if (!this.isSupported()) {
|
|
134
|
+
return () => {};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Store handler
|
|
138
|
+
if (!this._messageHandlers.has(type)) {
|
|
139
|
+
this._messageHandlers.set(type, new Set());
|
|
140
|
+
}
|
|
141
|
+
this._messageHandlers.get(type).add(handler);
|
|
142
|
+
|
|
143
|
+
// Set up global message listener if not already done
|
|
144
|
+
if (this._messageHandlers.size === 1) {
|
|
145
|
+
navigator.serviceWorker.addEventListener('message', this._handleMessage.bind(this));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Return unsubscribe function
|
|
149
|
+
return () => {
|
|
150
|
+
const handlers = this._messageHandlers.get(type);
|
|
151
|
+
if (handlers) {
|
|
152
|
+
handlers.delete(handler);
|
|
153
|
+
if (handlers.size === 0) {
|
|
154
|
+
this._messageHandlers.delete(type);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Get service worker state
|
|
161
|
+
getState() {
|
|
162
|
+
if (!this._registration) {
|
|
163
|
+
return 'none';
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (this._registration.installing) {
|
|
167
|
+
return 'installing';
|
|
168
|
+
} else if (this._registration.waiting) {
|
|
169
|
+
return 'waiting';
|
|
170
|
+
} else if (this._registration.active) {
|
|
171
|
+
return 'active';
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return 'unknown';
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Private: Handle incoming messages
|
|
178
|
+
_handleMessage(event) {
|
|
179
|
+
const { type, ...data } = event.data || {};
|
|
180
|
+
|
|
181
|
+
if (!type) return;
|
|
182
|
+
|
|
183
|
+
const handlers = this._messageHandlers.get(type);
|
|
184
|
+
if (handlers) {
|
|
185
|
+
handlers.forEach(handler => {
|
|
186
|
+
try {
|
|
187
|
+
handler(data, event);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
console.error('Message handler error:', error);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export default ServiceWorker;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import lodash from 'lodash';
|
|
2
|
+
const { get: _get, set: _set } = lodash;
|
|
3
|
+
|
|
4
|
+
class Storage {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.storageKey = '_manager';
|
|
7
|
+
this.pseudoStorage = {};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
get(path, defaultValue) {
|
|
11
|
+
let usableStorage;
|
|
12
|
+
|
|
13
|
+
// Try to parse the localStorage object
|
|
14
|
+
try {
|
|
15
|
+
usableStorage = JSON.parse(window.localStorage.getItem(this.storageKey) || '{}');
|
|
16
|
+
} catch (e) {
|
|
17
|
+
usableStorage = this.pseudoStorage;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// If there's no path, return the entire storage object
|
|
21
|
+
if (!path) {
|
|
22
|
+
return usableStorage || defaultValue;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Return the value at the path
|
|
26
|
+
return _get(usableStorage, path, defaultValue);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
set(path, value) {
|
|
30
|
+
let usableStorage;
|
|
31
|
+
|
|
32
|
+
// Try to get the current storage
|
|
33
|
+
try {
|
|
34
|
+
usableStorage = this.get();
|
|
35
|
+
} catch (e) {
|
|
36
|
+
usableStorage = this.pseudoStorage;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// If there's no path, replace the entire storage object
|
|
40
|
+
if (!path) {
|
|
41
|
+
usableStorage = value || {};
|
|
42
|
+
} else {
|
|
43
|
+
// Set the value at the path
|
|
44
|
+
_set(usableStorage, path, value);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Try to set the localStorage object
|
|
48
|
+
try {
|
|
49
|
+
window.localStorage.setItem(this.storageKey, JSON.stringify(usableStorage));
|
|
50
|
+
} catch (e) {
|
|
51
|
+
this.pseudoStorage = usableStorage;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return usableStorage;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
remove(path) {
|
|
58
|
+
if (!path) {
|
|
59
|
+
this.clear();
|
|
60
|
+
} else {
|
|
61
|
+
this.set(path, undefined);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
clear() {
|
|
66
|
+
try {
|
|
67
|
+
window.localStorage.setItem(this.storageKey, '{}');
|
|
68
|
+
} catch (e) {
|
|
69
|
+
this.pseudoStorage = {};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Session storage methods
|
|
74
|
+
session = {
|
|
75
|
+
get: (path, defaultValue) => {
|
|
76
|
+
let usableStorage;
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
usableStorage = JSON.parse(window.sessionStorage.getItem(this.storageKey) || '{}');
|
|
80
|
+
} catch (e) {
|
|
81
|
+
return defaultValue;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!path) {
|
|
85
|
+
return usableStorage || defaultValue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return _get(usableStorage, path, defaultValue);
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
set: (path, value) => {
|
|
92
|
+
let usableStorage;
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
usableStorage = this.session.get();
|
|
96
|
+
} catch (e) {
|
|
97
|
+
usableStorage = {};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!path) {
|
|
101
|
+
usableStorage = value || {};
|
|
102
|
+
} else {
|
|
103
|
+
_set(usableStorage, path, value);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
window.sessionStorage.setItem(this.storageKey, JSON.stringify(usableStorage));
|
|
108
|
+
} catch (e) {
|
|
109
|
+
// Silent fail
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return usableStorage;
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
remove: (path) => {
|
|
116
|
+
if (!path) {
|
|
117
|
+
this.session.clear();
|
|
118
|
+
} else {
|
|
119
|
+
this.session.set(path, undefined);
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
clear: () => {
|
|
124
|
+
try {
|
|
125
|
+
window.sessionStorage.setItem(this.storageKey, '{}');
|
|
126
|
+
} catch (e) {
|
|
127
|
+
// Silent fail
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export default Storage;
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
// Unit multipliers
|
|
2
|
+
const UNITS = {
|
|
3
|
+
milliseconds: 1,
|
|
4
|
+
seconds: 1000,
|
|
5
|
+
minutes: 1000 * 60,
|
|
6
|
+
hours: 1000 * 60 * 60,
|
|
7
|
+
days: 1000 * 60 * 60 * 24,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
// Storage key
|
|
11
|
+
const STORAGE_KEY = 'wm_usage';
|
|
12
|
+
|
|
13
|
+
// Session timeout (30 minutes of inactivity = new session)
|
|
14
|
+
const SESSION_TIMEOUT = 30 * 60 * 1000;
|
|
15
|
+
|
|
16
|
+
class Usage {
|
|
17
|
+
constructor(manager) {
|
|
18
|
+
this.manager = manager;
|
|
19
|
+
this.data = null;
|
|
20
|
+
this.initialized = false;
|
|
21
|
+
this.isNewVersion = false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Check if we're in a browser extension context
|
|
25
|
+
_isExtension() {
|
|
26
|
+
return this.manager.utilities().getRuntime() === 'browser-extension';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Get extension storage API
|
|
30
|
+
_getExtensionStorage() {
|
|
31
|
+
if (typeof chrome !== 'undefined' && chrome.storage?.local) {
|
|
32
|
+
return chrome.storage.local;
|
|
33
|
+
}
|
|
34
|
+
if (typeof browser !== 'undefined' && browser.storage?.local) {
|
|
35
|
+
return browser.storage.local;
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Initialize - loads or creates usage data (async for extensions)
|
|
41
|
+
async initialize() {
|
|
42
|
+
// Skip if already initialized
|
|
43
|
+
if (this.initialized) {
|
|
44
|
+
return this.data;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Load existing data based on runtime
|
|
48
|
+
let existing = null;
|
|
49
|
+
|
|
50
|
+
if (this._isExtension()) {
|
|
51
|
+
existing = await this._loadFromExtensionStorage();
|
|
52
|
+
} else {
|
|
53
|
+
existing = this._loadFromLocalStorage();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const now = Date.now();
|
|
57
|
+
const currentVersion = this.manager.config?.version || null;
|
|
58
|
+
|
|
59
|
+
if (existing) {
|
|
60
|
+
this.data = existing;
|
|
61
|
+
|
|
62
|
+
// Check if this is a new session (last activity was more than SESSION_TIMEOUT ago)
|
|
63
|
+
const timeSinceLastActive = now - (this.data.lastActive || 0);
|
|
64
|
+
if (timeSinceLastActive > SESSION_TIMEOUT) {
|
|
65
|
+
this.data.session.count = (this.data.session?.count || 0) + 1;
|
|
66
|
+
this.data.session.started = now;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Update lastActive
|
|
70
|
+
this.data.lastActive = now;
|
|
71
|
+
|
|
72
|
+
// Check for version change
|
|
73
|
+
if (currentVersion && this.data.version?.current !== currentVersion) {
|
|
74
|
+
this.data.version = this.data.version || {};
|
|
75
|
+
this.data.version.previous = this.data.version.current;
|
|
76
|
+
this.data.version.current = currentVersion;
|
|
77
|
+
this.isNewVersion = true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
await this._save();
|
|
81
|
+
} else {
|
|
82
|
+
// First time usage
|
|
83
|
+
this.data = {
|
|
84
|
+
installed: now,
|
|
85
|
+
lastActive: now,
|
|
86
|
+
session: {
|
|
87
|
+
started: now,
|
|
88
|
+
count: 1,
|
|
89
|
+
},
|
|
90
|
+
version: {
|
|
91
|
+
initial: currentVersion,
|
|
92
|
+
current: currentVersion,
|
|
93
|
+
previous: null,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
await this._save();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
this.initialized = true;
|
|
100
|
+
return this.data;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Load from extension storage (async)
|
|
104
|
+
async _loadFromExtensionStorage() {
|
|
105
|
+
const storage = this._getExtensionStorage();
|
|
106
|
+
if (!storage) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
const result = await storage.get(STORAGE_KEY);
|
|
112
|
+
return result[STORAGE_KEY] || null;
|
|
113
|
+
} catch (e) {
|
|
114
|
+
console.warn('[Usage] Failed to load from extension storage:', e);
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Load from localStorage (sync)
|
|
120
|
+
_loadFromLocalStorage() {
|
|
121
|
+
try {
|
|
122
|
+
const data = localStorage.getItem(STORAGE_KEY);
|
|
123
|
+
return data ? JSON.parse(data) : null;
|
|
124
|
+
} catch (e) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Save data to storage
|
|
130
|
+
async _save() {
|
|
131
|
+
if (this._isExtension()) {
|
|
132
|
+
await this._saveToExtensionStorage();
|
|
133
|
+
} else {
|
|
134
|
+
this._saveToLocalStorage();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return this.data;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Save to extension storage (async)
|
|
141
|
+
async _saveToExtensionStorage() {
|
|
142
|
+
const storage = this._getExtensionStorage();
|
|
143
|
+
if (!storage) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
await storage.set({ [STORAGE_KEY]: this.data });
|
|
149
|
+
} catch (e) {
|
|
150
|
+
console.warn('[Usage] Failed to save to extension storage:', e);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Save to localStorage (sync)
|
|
155
|
+
_saveToLocalStorage() {
|
|
156
|
+
try {
|
|
157
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(this.data));
|
|
158
|
+
} catch (e) {
|
|
159
|
+
// localStorage not available
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Calculate duration from a timestamp in specified units
|
|
164
|
+
_calculateDuration(timestamp, unit) {
|
|
165
|
+
if (!timestamp) {
|
|
166
|
+
return 0;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Default to milliseconds
|
|
170
|
+
unit = unit || 'milliseconds';
|
|
171
|
+
|
|
172
|
+
// Get multiplier
|
|
173
|
+
const multiplier = UNITS[unit];
|
|
174
|
+
if (!multiplier) {
|
|
175
|
+
throw new Error(`Invalid unit: ${unit}. Valid units: ${Object.keys(UNITS).join(', ')}`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return (Date.now() - timestamp) / multiplier;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Get total usage duration in specified units (since installed)
|
|
182
|
+
getUsageDuration(unit) {
|
|
183
|
+
return this._calculateDuration(this.data?.installed, unit);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Get current session duration in specified units
|
|
187
|
+
getSessionDuration(unit) {
|
|
188
|
+
return this._calculateDuration(this.data?.session?.started, unit);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Get installed date
|
|
192
|
+
getInstalledDate() {
|
|
193
|
+
if (!this.data?.installed) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return new Date(this.data.installed);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Get session count
|
|
201
|
+
getSessionCount() {
|
|
202
|
+
return this.data?.session?.count || 0;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Reset usage data (for testing or user request)
|
|
206
|
+
async reset() {
|
|
207
|
+
const now = Date.now();
|
|
208
|
+
const currentVersion = this.manager.config?.version || null;
|
|
209
|
+
|
|
210
|
+
this.data = {
|
|
211
|
+
installed: now,
|
|
212
|
+
lastActive: now,
|
|
213
|
+
session: {
|
|
214
|
+
started: now,
|
|
215
|
+
count: 1,
|
|
216
|
+
},
|
|
217
|
+
version: {
|
|
218
|
+
initial: currentVersion,
|
|
219
|
+
current: currentVersion,
|
|
220
|
+
previous: null,
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
this.isNewVersion = false;
|
|
225
|
+
await this._save();
|
|
226
|
+
return this.data;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Get binding-friendly data object for bindings system
|
|
230
|
+
getBindingData() {
|
|
231
|
+
return {
|
|
232
|
+
installed: this.data?.installed || null,
|
|
233
|
+
lastActive: this.data?.lastActive || null,
|
|
234
|
+
session: {
|
|
235
|
+
started: this.data?.session?.started || null,
|
|
236
|
+
count: this.getSessionCount(),
|
|
237
|
+
},
|
|
238
|
+
version: {
|
|
239
|
+
initial: this.data?.version?.initial || null,
|
|
240
|
+
current: this.data?.version?.current || null,
|
|
241
|
+
previous: this.data?.version?.previous || null,
|
|
242
|
+
isNew: this.isNewVersion,
|
|
243
|
+
},
|
|
244
|
+
duration: {
|
|
245
|
+
total: {
|
|
246
|
+
milliseconds: this.getUsageDuration('milliseconds'),
|
|
247
|
+
seconds: this.getUsageDuration('seconds'),
|
|
248
|
+
minutes: this.getUsageDuration('minutes'),
|
|
249
|
+
hours: this.getUsageDuration('hours'),
|
|
250
|
+
days: this.getUsageDuration('days'),
|
|
251
|
+
},
|
|
252
|
+
session: {
|
|
253
|
+
milliseconds: this.getSessionDuration('milliseconds'),
|
|
254
|
+
seconds: this.getSessionDuration('seconds'),
|
|
255
|
+
minutes: this.getSessionDuration('minutes'),
|
|
256
|
+
hours: this.getSessionDuration('hours'),
|
|
257
|
+
days: this.getSessionDuration('days'),
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export default Usage;
|