browser-extension-manager 1.2.2 → 1.2.4
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 +104 -0
- package/README.md +109 -0
- package/dist/assets/themes/classy/css/base/_utilities.scss +0 -51
- package/dist/assets/themes/classy/css/components/_text.scss +11 -0
- package/dist/background.js +220 -9
- package/dist/content.js +2 -1
- package/dist/defaults/_.env +4 -0
- package/dist/defaults/config/browser-extension-manager.json +9 -11
- package/dist/gulp/main.js +4 -0
- package/dist/gulp/tasks/audit.js +1 -1
- package/dist/gulp/tasks/defaults.js +8 -1
- package/dist/gulp/tasks/distribute.js +6 -1
- package/dist/gulp/tasks/package.js +6 -2
- package/dist/gulp/tasks/publish.js +1 -0
- package/dist/gulp/tasks/webpack.js +8 -6
- package/dist/lib/auth-helpers.js +184 -0
- package/dist/lib/logger.js +1 -1
- package/dist/options.js +17 -0
- package/dist/page.js +17 -0
- package/dist/popup.js +17 -0
- package/dist/sidepanel.js +17 -0
- package/firebase-debug.log +280 -0
- package/package.json +9 -5
|
@@ -15,6 +15,9 @@ const config = Manager.getConfig('project');
|
|
|
15
15
|
const rootPathPackage = Manager.getRootPath('main');
|
|
16
16
|
const rootPathProject = Manager.getRootPath('project');
|
|
17
17
|
|
|
18
|
+
// Constants
|
|
19
|
+
const LOUD = process.env.BXM_LOUD_LOGS === 'true';
|
|
20
|
+
|
|
18
21
|
// Glob
|
|
19
22
|
const input = [
|
|
20
23
|
// Files to include
|
|
@@ -108,7 +111,9 @@ function customTransform() {
|
|
|
108
111
|
const relativePath = path.relative(file.base, file.path).replace(/\\/g, '/');
|
|
109
112
|
|
|
110
113
|
// Log
|
|
111
|
-
|
|
114
|
+
if (LOUD) {
|
|
115
|
+
logger.log(`Processing file: ${relativePath}`);
|
|
116
|
+
}
|
|
112
117
|
|
|
113
118
|
// Change path if it starts with 'pages/'
|
|
114
119
|
// if (relativePath.startsWith('pages/')) {
|
|
@@ -58,6 +58,7 @@ async function generateBuildJs(outputDir) {
|
|
|
58
58
|
},
|
|
59
59
|
config: {
|
|
60
60
|
// Core metadata
|
|
61
|
+
runtime: 'browser-extension',
|
|
61
62
|
version: project.version,
|
|
62
63
|
environment: Manager.getEnvironment(),
|
|
63
64
|
buildTime: Date.now(),
|
|
@@ -97,8 +98,11 @@ async function generateBuildJs(outputDir) {
|
|
|
97
98
|
refreshNewVersion: { enabled: true, config: {} },
|
|
98
99
|
serviceWorker: { enabled: false, config: {} },
|
|
99
100
|
|
|
100
|
-
//
|
|
101
|
-
|
|
101
|
+
// Tracking
|
|
102
|
+
tracking: {
|
|
103
|
+
'google-analytics': config.google_analytics?.id || '',
|
|
104
|
+
'google-analytics-secret': config.google_analytics?.secret || '',
|
|
105
|
+
},
|
|
102
106
|
|
|
103
107
|
// Theme config
|
|
104
108
|
theme: config.theme || {},
|
|
@@ -59,6 +59,7 @@ async function publish(complete) {
|
|
|
59
59
|
logger.log(' Chrome: CHROME_EXTENSION_ID, CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN');
|
|
60
60
|
logger.log(' Firefox: FIREFOX_EXTENSION_ID, FIREFOX_API_KEY, FIREFOX_API_SECRET');
|
|
61
61
|
logger.log(' Edge: EDGE_PRODUCT_ID, EDGE_CLIENT_ID, EDGE_API_KEY');
|
|
62
|
+
logger.log(' Opera: No API available - submit manually at https://addons.opera.com/developer/');
|
|
62
63
|
return complete();
|
|
63
64
|
}
|
|
64
65
|
|
|
@@ -49,12 +49,11 @@ const watchInput = [
|
|
|
49
49
|
`${rootPathPackage}/dist/assets/themes/**/*.js`,
|
|
50
50
|
'src/assets/themes/**/*.js',
|
|
51
51
|
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
`${rootPathPackage}/src
|
|
57
|
-
`${rootPathPackage}/src/index.js`,
|
|
52
|
+
// All project assets js - watch for changes but don't compile as entry points
|
|
53
|
+
'src/assets/js/**/*.js',
|
|
54
|
+
|
|
55
|
+
// All BEM package src files - watch for changes (includes background.js, popup.js, etc.)
|
|
56
|
+
`${rootPathPackage}/src/**/*.js`,
|
|
58
57
|
|
|
59
58
|
// So we can watch for changes while we're developing web-manager
|
|
60
59
|
`${rootPathPackage}/../web-manager/src`,
|
|
@@ -108,6 +107,9 @@ function getSettings() {
|
|
|
108
107
|
},
|
|
109
108
|
// Add module resolution paths
|
|
110
109
|
modules: [
|
|
110
|
+
// Local web-manager's node_modules (for when we're using "web-manager": "file:../web-manager")
|
|
111
|
+
path.resolve(rootPathPackage, '../web-manager/node_modules'),
|
|
112
|
+
|
|
111
113
|
// Package's node_modules
|
|
112
114
|
path.resolve(rootPathPackage, 'node_modules'),
|
|
113
115
|
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
// Auth helpers for cross-context auth sync in browser extensions
|
|
2
|
+
// Used by popup.js, options.js, sidepanel.js, page.js
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Sign in with custom token from storage
|
|
6
|
+
* @param {Object} context - The manager instance
|
|
7
|
+
* @param {Object} authState - Auth state with token
|
|
8
|
+
*/
|
|
9
|
+
async function signInWithStoredToken(context, authState) {
|
|
10
|
+
const { webManager, logger } = context;
|
|
11
|
+
|
|
12
|
+
// Skip if no token
|
|
13
|
+
if (!authState?.token) {
|
|
14
|
+
logger.log('[AUTH-SYNC] No token in auth state, skipping sign in');
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
logger.log('[AUTH-SYNC] Signing in with stored token...');
|
|
20
|
+
|
|
21
|
+
// Sign in using webManager's auth which initializes Firebase
|
|
22
|
+
await webManager.auth().signInWithCustomToken(authState.token);
|
|
23
|
+
|
|
24
|
+
logger.log('[AUTH-SYNC] Signed in successfully:', authState.user?.email);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
// Token may have expired, clear it
|
|
27
|
+
logger.error('[AUTH-SYNC] Error signing in with token:', error.message);
|
|
28
|
+
|
|
29
|
+
// If token is invalid/expired, clear the auth state
|
|
30
|
+
if (error.code === 'auth/invalid-custom-token' || error.code === 'auth/custom-token-expired') {
|
|
31
|
+
logger.log('[AUTH-SYNC] Token expired, clearing auth state');
|
|
32
|
+
context.extension.storage.remove('bxm:authState');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Set up storage listener for cross-context auth sync
|
|
39
|
+
* Listens for auth state changes from background.js and syncs Firebase auth
|
|
40
|
+
* @param {Object} context - The manager instance (must have extension, webManager, logger)
|
|
41
|
+
*/
|
|
42
|
+
export function setupAuthStorageListener(context) {
|
|
43
|
+
const { extension, webManager, logger } = context;
|
|
44
|
+
|
|
45
|
+
// Check existing auth state on load and sign in
|
|
46
|
+
extension.storage.get('bxm:authState', (result) => {
|
|
47
|
+
const authState = result['bxm:authState'];
|
|
48
|
+
|
|
49
|
+
if (authState?.token) {
|
|
50
|
+
logger.log('[AUTH-SYNC] Found existing auth state, signing in...', authState.user?.email);
|
|
51
|
+
signInWithStoredToken(context, authState);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Listen for WM auth state changes and sync to storage
|
|
56
|
+
// When user signs out via WM, clear storage so background.js knows
|
|
57
|
+
webManager.auth().listen((state) => {
|
|
58
|
+
if (!state.user) {
|
|
59
|
+
// User signed out - clear storage so all contexts sync
|
|
60
|
+
logger.log('[AUTH-SYNC] WM auth signed out, clearing storage...');
|
|
61
|
+
extension.storage.remove('bxm:authState');
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Listen for storage changes from background.js
|
|
66
|
+
// Note: BEM normalizes storage to sync or local, so we listen to all areas
|
|
67
|
+
extension.storage.onChanged.addListener((changes) => {
|
|
68
|
+
// Check for auth state change
|
|
69
|
+
const authChange = changes['bxm:authState'];
|
|
70
|
+
if (!authChange) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Log
|
|
75
|
+
logger.log('[AUTH-SYNC] Auth state changed in storage:', authChange);
|
|
76
|
+
|
|
77
|
+
// Get the new auth state
|
|
78
|
+
const newAuthState = authChange.newValue;
|
|
79
|
+
|
|
80
|
+
// If auth state was cleared (signed out)
|
|
81
|
+
if (!newAuthState) {
|
|
82
|
+
logger.log('[AUTH-SYNC] Auth state cleared, signing out...');
|
|
83
|
+
webManager.auth().signOut();
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Sign in with the new token
|
|
88
|
+
if (newAuthState?.token) {
|
|
89
|
+
signInWithStoredToken(context, newAuthState);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Log
|
|
94
|
+
logger.log('Auth storage listener set up');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Open auth page in new tab (for signing in via website)
|
|
99
|
+
* @param {Object} context - The manager instance (must have extension, webManager, logger)
|
|
100
|
+
* @param {Object} options - Options object
|
|
101
|
+
* @param {string} options.path - Path to open (default: '/token')
|
|
102
|
+
* @param {string} options.authReturnUrl - Return URL for electron/deep links
|
|
103
|
+
*/
|
|
104
|
+
export function openAuthPage(context, options = {}) {
|
|
105
|
+
const { extension, webManager, logger } = context;
|
|
106
|
+
|
|
107
|
+
// Get auth domain from config
|
|
108
|
+
const authDomain = webManager.config?.firebase?.app?.config?.authDomain;
|
|
109
|
+
|
|
110
|
+
if (!authDomain) {
|
|
111
|
+
logger.error('No authDomain configured');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Build the URL
|
|
116
|
+
const path = options.path || '/token';
|
|
117
|
+
const authUrl = new URL(path, `https://${authDomain}`);
|
|
118
|
+
|
|
119
|
+
// Add return URL if provided (for electron/deep links)
|
|
120
|
+
if (options.authReturnUrl) {
|
|
121
|
+
authUrl.searchParams.set('authReturnUrl', options.authReturnUrl);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Log
|
|
125
|
+
logger.log('Opening auth page:', authUrl.toString());
|
|
126
|
+
|
|
127
|
+
// Get current active tab so we can restore it after auth
|
|
128
|
+
extension.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
|
129
|
+
const authSourceTabId = tabs[0]?.id;
|
|
130
|
+
|
|
131
|
+
// Add source tab ID to URL so background can restore it
|
|
132
|
+
if (authSourceTabId) {
|
|
133
|
+
authUrl.searchParams.set('authSourceTabId', authSourceTabId);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Open in new tab
|
|
137
|
+
extension.tabs.create({ url: authUrl.toString() });
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Set up DOM event listeners for auth buttons (sign in, account)
|
|
143
|
+
* Uses event delegation so it works with dynamically rendered content
|
|
144
|
+
* @param {Object} context - The manager instance (must have extension, webManager, logger)
|
|
145
|
+
*/
|
|
146
|
+
export function setupAuthEventListeners(context) {
|
|
147
|
+
// Only set up once DOM is ready
|
|
148
|
+
if (typeof document === 'undefined') {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Sign in button (.auth-signin-btn) - opens auth page
|
|
153
|
+
document.addEventListener('click', (event) => {
|
|
154
|
+
const $signInBtn = event.target.closest('.auth-signin-btn');
|
|
155
|
+
if (!$signInBtn) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
event.preventDefault();
|
|
160
|
+
event.stopPropagation();
|
|
161
|
+
|
|
162
|
+
openAuthPage(context);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Account button (.auth-account-btn) - opens account page on website
|
|
166
|
+
document.addEventListener('click', (event) => {
|
|
167
|
+
const $accountBtn = event.target.closest('.auth-account-btn');
|
|
168
|
+
if (!$accountBtn) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
event.preventDefault();
|
|
173
|
+
event.stopPropagation();
|
|
174
|
+
|
|
175
|
+
openAuthPage(context, { path: '/account' });
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Note: .auth-signout-btn is handled by web-manager's auth module
|
|
179
|
+
// BEM's storage listener will detect the sign-out via onAuthStateChanged in background.js
|
|
180
|
+
// If background hasn't initialized Firebase yet, stale storage is cleared on next auth attempt
|
|
181
|
+
|
|
182
|
+
// Log
|
|
183
|
+
context.logger.log('Auth event listeners set up');
|
|
184
|
+
}
|
package/dist/lib/logger.js
CHANGED
package/dist/options.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Manager as WebManager } from 'web-manager';
|
|
3
3
|
import extension from './lib/extension.js';
|
|
4
4
|
import LoggerLite from './lib/logger-lite.js';
|
|
5
|
+
import { setupAuthStorageListener, setupAuthEventListeners, openAuthPage as openAuthPageHelper } from './lib/auth-helpers.js';
|
|
5
6
|
|
|
6
7
|
// Import theme (exposes Bootstrap to window.bootstrap)
|
|
7
8
|
import '__theme__/_theme.js';
|
|
@@ -29,12 +30,28 @@ class Manager {
|
|
|
29
30
|
// Initialize
|
|
30
31
|
await this.webManager.initialize(configuration);
|
|
31
32
|
|
|
33
|
+
// Set up auth state listener (updates bindings with user/account state)
|
|
34
|
+
this.webManager.auth().listen((state) => {
|
|
35
|
+
this.logger.log('Auth state changed:', state);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Set up storage listener for cross-context auth sync
|
|
39
|
+
setupAuthStorageListener(this);
|
|
40
|
+
|
|
41
|
+
// Set up auth event listeners (sign in, account buttons)
|
|
42
|
+
setupAuthEventListeners(this);
|
|
43
|
+
|
|
32
44
|
// Log
|
|
33
45
|
this.logger.log('Initialized!', this);
|
|
34
46
|
|
|
35
47
|
// Return manager instance
|
|
36
48
|
return this;
|
|
37
49
|
}
|
|
50
|
+
|
|
51
|
+
// Open auth page in new tab (for signing in via website)
|
|
52
|
+
openAuthPage(options = {}) {
|
|
53
|
+
openAuthPageHelper(this, options);
|
|
54
|
+
}
|
|
38
55
|
}
|
|
39
56
|
|
|
40
57
|
// Export
|
package/dist/page.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Manager as WebManager } from 'web-manager';
|
|
3
3
|
import extension from './lib/extension.js';
|
|
4
4
|
import LoggerLite from './lib/logger-lite.js';
|
|
5
|
+
import { setupAuthStorageListener, setupAuthEventListeners, openAuthPage as openAuthPageHelper } from './lib/auth-helpers.js';
|
|
5
6
|
|
|
6
7
|
// Import theme (exposes Bootstrap to window.bootstrap)
|
|
7
8
|
import '__theme__/_theme.js';
|
|
@@ -29,12 +30,28 @@ class Manager {
|
|
|
29
30
|
// Initialize
|
|
30
31
|
await this.webManager.initialize(configuration);
|
|
31
32
|
|
|
33
|
+
// Set up auth state listener (updates bindings with user/account state)
|
|
34
|
+
this.webManager.auth().listen((state) => {
|
|
35
|
+
this.logger.log('Auth state changed:', state);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Set up storage listener for cross-context auth sync
|
|
39
|
+
setupAuthStorageListener(this);
|
|
40
|
+
|
|
41
|
+
// Set up auth event listeners (sign in, account buttons)
|
|
42
|
+
setupAuthEventListeners(this);
|
|
43
|
+
|
|
32
44
|
// Log
|
|
33
45
|
this.logger.log('Initialized!', this);
|
|
34
46
|
|
|
35
47
|
// Return manager instance
|
|
36
48
|
return this;
|
|
37
49
|
}
|
|
50
|
+
|
|
51
|
+
// Open auth page in new tab (for signing in via website)
|
|
52
|
+
openAuthPage(options = {}) {
|
|
53
|
+
openAuthPageHelper(this, options);
|
|
54
|
+
}
|
|
38
55
|
}
|
|
39
56
|
|
|
40
57
|
// Export
|
package/dist/popup.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Manager as WebManager } from 'web-manager';
|
|
3
3
|
import extension from './lib/extension.js';
|
|
4
4
|
import LoggerLite from './lib/logger-lite.js';
|
|
5
|
+
import { setupAuthStorageListener, setupAuthEventListeners, openAuthPage as openAuthPageHelper } from './lib/auth-helpers.js';
|
|
5
6
|
|
|
6
7
|
// Import theme (exposes Bootstrap to window.bootstrap)
|
|
7
8
|
import '__theme__/_theme.js';
|
|
@@ -29,12 +30,28 @@ class Manager {
|
|
|
29
30
|
// Initialize
|
|
30
31
|
await this.webManager.initialize(configuration);
|
|
31
32
|
|
|
33
|
+
// Set up auth state listener (updates bindings with user/account state)
|
|
34
|
+
this.webManager.auth().listen((state) => {
|
|
35
|
+
this.logger.log('Auth state changed:', state);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Set up storage listener for cross-context auth sync
|
|
39
|
+
setupAuthStorageListener(this);
|
|
40
|
+
|
|
41
|
+
// Set up auth event listeners (sign in, account buttons)
|
|
42
|
+
setupAuthEventListeners(this);
|
|
43
|
+
|
|
32
44
|
// Log
|
|
33
45
|
this.logger.log('Initialized!', this);
|
|
34
46
|
|
|
35
47
|
// Return manager instance
|
|
36
48
|
return this;
|
|
37
49
|
}
|
|
50
|
+
|
|
51
|
+
// Open auth page in new tab (for signing in via website)
|
|
52
|
+
openAuthPage(options = {}) {
|
|
53
|
+
openAuthPageHelper(this, options);
|
|
54
|
+
}
|
|
38
55
|
}
|
|
39
56
|
|
|
40
57
|
// Export
|
package/dist/sidepanel.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Manager as WebManager } from 'web-manager';
|
|
3
3
|
import extension from './lib/extension.js';
|
|
4
4
|
import LoggerLite from './lib/logger-lite.js';
|
|
5
|
+
import { setupAuthStorageListener, setupAuthEventListeners, openAuthPage as openAuthPageHelper } from './lib/auth-helpers.js';
|
|
5
6
|
|
|
6
7
|
// Import theme (exposes Bootstrap to window.bootstrap)
|
|
7
8
|
import '__theme__/_theme.js';
|
|
@@ -29,12 +30,28 @@ class Manager {
|
|
|
29
30
|
// Initialize
|
|
30
31
|
await this.webManager.initialize(configuration);
|
|
31
32
|
|
|
33
|
+
// Set up auth state listener (updates bindings with user/account state)
|
|
34
|
+
this.webManager.auth().listen((state) => {
|
|
35
|
+
this.logger.log('Auth state changed:', state);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Set up storage listener for cross-context auth sync
|
|
39
|
+
setupAuthStorageListener(this);
|
|
40
|
+
|
|
41
|
+
// Set up auth event listeners (sign in, account buttons)
|
|
42
|
+
setupAuthEventListeners(this);
|
|
43
|
+
|
|
32
44
|
// Log
|
|
33
45
|
this.logger.log('Initialized!', this);
|
|
34
46
|
|
|
35
47
|
// Return manager instance
|
|
36
48
|
return this;
|
|
37
49
|
}
|
|
50
|
+
|
|
51
|
+
// Open auth page in new tab (for signing in via website)
|
|
52
|
+
openAuthPage(options = {}) {
|
|
53
|
+
openAuthPageHelper(this, options);
|
|
54
|
+
}
|
|
38
55
|
}
|
|
39
56
|
|
|
40
57
|
// Export
|