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 CHANGED
@@ -323,6 +323,105 @@ Manager.initialize().then(() => {
323
323
  **Storage normalization:**
324
324
  The wrapper automatically resolves `storage` to `storage.sync` if available, falling back to `storage.local`.
325
325
 
326
+ **Auth Helpers:** [src/lib/auth-helpers.js](src/lib/auth-helpers.js)
327
+
328
+ Provides cross-context auth synchronization and reusable auth UI event handlers.
329
+
330
+ ### Cross-Context Auth Architecture
331
+
332
+ **Background.js is the source of truth** for authentication. Browser extensions have multiple isolated JavaScript contexts (background, popup, options, pages, sidepanel) - each runs its own Firebase instance. BEM solves this by:
333
+
334
+ 1. Background.js monitors for auth tokens via `tabs.onUpdated`
335
+ 2. When detected, background signs in Firebase and saves to `chrome.storage`
336
+ 3. Other contexts listen to storage changes and sync their Firebase instances
337
+
338
+ **Sign-in Flow:**
339
+ ```
340
+ User clicks .auth-signin-btn
341
+ → openAuthPage() opens https://{authDomain}/token?authSourceTabId=123
342
+ → Website authenticates, redirects to /token?authToken=xxx
343
+ → background.js tabs.onUpdated detects authDomain URL with authToken param
344
+ → background.js calls handleAuthToken():
345
+ → signInWithCustomToken(auth, token)
346
+ → Saves {token, user, timestamp} to storage key 'bxm:authState'
347
+ → Closes the /token tab
348
+ → Reactivates the original tab (authSourceTabId)
349
+ → Other contexts detect storage change via setupAuthStorageListener()
350
+ → Each context calls signInWithStoredToken() to sign in their Firebase
351
+ ```
352
+
353
+ **Sign-out Flow:**
354
+ ```
355
+ User clicks .auth-signout-btn
356
+ → Web Manager signs out Firebase locally
357
+ → setupAuthStorageListener() detects WM auth state change (user=null)
358
+ → Clears 'bxm:authState' from storage
359
+ → background.js setupAuthStorageListener() detects storage cleared
360
+ → background.js signs out its Firebase instance
361
+ → Other contexts detect storage cleared and sign out
362
+ ```
363
+
364
+ **Key Implementation Details:**
365
+
366
+ 1. **Storage area**: BEM normalizes storage to `sync` (if available) or `local`. Auth listeners should NOT check `areaName` - just check for the `bxm:authState` key directly.
367
+
368
+ 2. **Firebase in service workers**: Static ES6 imports are required. Dynamic `import()` fails with webpack chunking in service workers.
369
+
370
+ 3. **Config path**: `authDomain` is at `config.firebase.app.config.authDomain` (loaded via BEM_BUILD_JSON).
371
+
372
+ 4. **Required permission**: `tabs` permission needed for `tabs.onUpdated` listener.
373
+
374
+ **Functions:**
375
+ - `setupAuthStorageListener(context)` - Listens for auth state changes from background.js AND monitors WM auth state to clear storage on sign-out
376
+ - `setupAuthEventListeners(context)` - Sets up delegated click handlers for auth buttons
377
+ - `openAuthPage(context, options)` - Opens auth page on the website with authSourceTabId for tab restoration
378
+
379
+ **Auth Button CSS Classes:**
380
+
381
+ Add these classes to your HTML elements to enable automatic auth handling:
382
+
383
+ | Class | Description | Action |
384
+ |-------|-------------|--------|
385
+ | `.auth-signin-btn` | Sign in button | Opens `/token` page on website (authDomain) |
386
+ | `.auth-signout-btn` | Sign out button | Handled by Web Manager (triggers storage sync via auth listener) |
387
+ | `.auth-account-btn` | Account button | Opens `/account` page on website |
388
+
389
+ **Example usage:**
390
+ ```html
391
+ <!-- Sign In Button (shown when logged out) -->
392
+ <button class="btn auth-signin-btn" data-wm-bind="@show !auth.user">
393
+ Sign In
394
+ </button>
395
+
396
+ <!-- Account Dropdown (shown when logged in) -->
397
+ <div data-wm-bind="@show auth.user" hidden>
398
+ <a class="auth-account-btn" href="#">Account</a>
399
+ <button class="auth-signout-btn">Sign Out</button>
400
+ </div>
401
+ ```
402
+
403
+ **Reactive bindings:**
404
+ - `data-wm-bind="@show auth.user"` - Show when logged in
405
+ - `data-wm-bind="@show !auth.user"` - Show when logged out
406
+ - `data-wm-bind="@text auth.user.displayName"` - Display user's name
407
+ - `data-wm-bind="@text auth.user.email"` - Display user's email
408
+ - `data-wm-bind="@attr src auth.user.photoURL"` - Set avatar image src
409
+
410
+ **Storage Schema (`bxm:authState`):**
411
+ ```javascript
412
+ {
413
+ token: "firebase-custom-token", // Used by other contexts to sign in
414
+ user: {
415
+ uid: "...",
416
+ email: "...",
417
+ displayName: "...",
418
+ photoURL: "...",
419
+ emailVerified: true
420
+ },
421
+ timestamp: 1234567890
422
+ }
423
+ ```
424
+
326
425
  **Logger:** [src/lib/logger.js](src/lib/logger.js)
327
426
  - Full logging utility
328
427
  - [src/lib/logger-lite.js](src/lib/logger-lite.js) for lightweight contexts
@@ -679,9 +778,14 @@ Manager.initialize().then(() => {
679
778
 
680
779
  **Utilities:**
681
780
  - [src/lib/extension.js](src/lib/extension.js) - Cross-browser API wrapper
781
+ - [src/lib/auth-helpers.js](src/lib/auth-helpers.js) - Cross-context auth sync (see Auth Architecture section above)
682
782
  - [src/lib/logger.js](src/lib/logger.js) - Logging utility
683
783
  - [src/cli.js](src/cli.js) - CLI implementation
684
784
 
785
+ **Auth System (Cross-Context):**
786
+ - [src/background.js](src/background.js) - Source of truth for auth; `setupAuthTokenListener()`, `setupAuthStorageListener()`, `handleAuthToken()`
787
+ - [src/lib/auth-helpers.js](src/lib/auth-helpers.js) - `setupAuthStorageListener()`, `openAuthPage()`, `setupAuthEventListeners()` for non-background contexts
788
+
685
789
  **CSS Framework:**
686
790
  - [src/assets/css/browser-extension-manager.scss](src/assets/css/browser-extension-manager.scss) - Main entry
687
791
  - [src/assets/css/core/](src/assets/css/core/) - Core styles
package/README.md CHANGED
@@ -86,6 +86,115 @@ EDGE_API_KEY="your-api-key"
86
86
 
87
87
  Only stores with configured credentials will be published to.
88
88
 
89
+ ## 🔐 Authentication
90
+
91
+ BEM provides built-in authentication support that syncs across all extension contexts (popup, options, pages, sidepanel, background).
92
+
93
+ ### Auth Architecture Overview
94
+
95
+ **Background.js is the source of truth** for authentication state. When a user signs in via the website, the auth token flows through background.js to all other contexts via `chrome.storage`.
96
+
97
+ ```
98
+ ┌─────────────────────────────────────────────────────────────────────────┐
99
+ │ SIGN-IN FLOW │
100
+ ├─────────────────────────────────────────────────────────────────────────┤
101
+ │ 1. User clicks .auth-signin-btn in any context │
102
+ │ 2. Extension opens https://{authDomain}/token?authSourceTabId=123 │
103
+ │ 3. Website authenticates user, redirects to /token?authToken=xxx │
104
+ │ 4. Background.js detects URL via tabs.onUpdated listener │
105
+ │ 5. Background signs in Firebase with custom token │
106
+ │ 6. Background saves auth state to chrome.storage (bxm:authState) │
107
+ │ 7. Background closes /token tab and reactivates original tab │
108
+ │ 8. Other contexts detect storage change and sign in their Firebase │
109
+ └─────────────────────────────────────────────────────────────────────────┘
110
+
111
+ ┌─────────────────────────────────────────────────────────────────────────┐
112
+ │ SIGN-OUT FLOW │
113
+ ├─────────────────────────────────────────────────────────────────────────┤
114
+ │ 1. User clicks .auth-signout-btn in any context │
115
+ │ 2. Web Manager signs out Firebase locally │
116
+ │ 3. Auth helper detects WM auth change, clears bxm:authState storage │
117
+ │ 4. Background.js detects storage cleared, signs out its Firebase │
118
+ │ 5. Other contexts detect storage change and sign out │
119
+ └─────────────────────────────────────────────────────────────────────────┘
120
+ ```
121
+
122
+ ### Required Configuration
123
+
124
+ Add `authDomain` to your Firebase config in `config/browser-extension-manager.json`:
125
+
126
+ ```json
127
+ {
128
+ "firebaseConfig": {
129
+ "apiKey": "...",
130
+ "authDomain": "your-app.firebaseapp.com",
131
+ "projectId": "..."
132
+ }
133
+ }
134
+ ```
135
+
136
+ ### Required Permission
137
+
138
+ Add the `tabs` permission to your `src/manifest.json`:
139
+
140
+ ```json
141
+ {
142
+ "permissions": ["tabs"]
143
+ }
144
+ ```
145
+
146
+ This is required for background.js to monitor tab URL changes and detect auth tokens.
147
+
148
+ ### Auth Button Classes
149
+
150
+ Add these classes to your HTML elements to enable automatic auth handling:
151
+
152
+ | Class | Description | Action |
153
+ |-------|-------------|--------|
154
+ | `.auth-signin-btn` | Sign in button | Opens `/token` page on website |
155
+ | `.auth-signout-btn` | Sign out button | Signs out via Web Manager (which triggers storage sync) |
156
+ | `.auth-account-btn` | Account button | Opens `/account` page on website |
157
+
158
+ ### Example
159
+ ```html
160
+ <!-- Sign In Button (shown when logged out) -->
161
+ <button class="btn auth-signin-btn" data-wm-bind="@show !auth.user">
162
+ Sign In
163
+ </button>
164
+
165
+ <!-- Account Section (shown when logged in) -->
166
+ <div data-wm-bind="@show auth.user" hidden>
167
+ <span data-wm-bind="@text auth.user.displayName">User</span>
168
+ <a class="auth-account-btn" href="#">Account</a>
169
+ <button class="auth-signout-btn">Sign Out</button>
170
+ </div>
171
+ ```
172
+
173
+ ### Reactive Bindings
174
+ - `data-wm-bind="@show auth.user"` - Show when logged in
175
+ - `data-wm-bind="@show !auth.user"` - Show when logged out
176
+ - `data-wm-bind="@text auth.user.displayName"` - Display user's name
177
+ - `data-wm-bind="@text auth.user.email"` - Display user's email
178
+ - `data-wm-bind="@attr src auth.user.photoURL"` - Set avatar image src
179
+
180
+ ### Storage Key
181
+
182
+ Auth state is stored in `chrome.storage` under the key `bxm:authState`:
183
+
184
+ ```javascript
185
+ {
186
+ token: "firebase-custom-token",
187
+ user: {
188
+ uid: "...",
189
+ email: "...",
190
+ displayName: "...",
191
+ photoURL: "...",
192
+ emailVerified: true
193
+ },
194
+ timestamp: 1234567890
195
+ }
196
+ ```
197
+
89
198
  <!-- ## ⛳️ Flags
90
199
  * `--test=false` - Coming soon
91
200
  ```bash
@@ -1,57 +1,6 @@
1
1
  // Classy Utility Classes
2
2
  // Custom helper classes and theme-specific utilities
3
3
 
4
- // ============================================
5
- // Section Spacing
6
- // ============================================
7
-
8
- // Section Spacing
9
- // .section {
10
- // padding: $classy-spacing-4xl 0;
11
-
12
- // &.section-sm {
13
- // padding: $classy-spacing-2xl 0;
14
- // }
15
-
16
- // &.section-lg {
17
- // padding: 6rem 0;
18
- // }
19
-
20
- // &.section-xl {
21
- // padding: 8rem 0;
22
- // }
23
- // }
24
-
25
- // Hero Sections
26
- // .hero {
27
- // padding: 6rem 0;
28
- // position: relative;
29
- // overflow: hidden;
30
-
31
- // &.hero-gradient {
32
- // background: $classy-gradient-primary;
33
- // }
34
-
35
- // &.hero-pattern {
36
- // &::before {
37
- // content: "";
38
- // position: absolute;
39
- // top: 0;
40
- // left: 0;
41
- // right: 0;
42
- // bottom: 0;
43
- // background-image: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%235B47FB' fill-opacity='0.1'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
44
- // opacity: 0.1;
45
- // z-index: 0;
46
- // }
47
- // }
48
-
49
- // .hero-content {
50
- // position: relative;
51
- // z-index: 1;
52
- // }
53
- // }
54
-
55
4
  // ============================================
56
5
  // Shadow Utilities
57
6
  // ============================================
@@ -39,3 +39,14 @@
39
39
  [data-bs-theme="dark"] .text-adaptive {
40
40
  @extend .text-dark;
41
41
  }
42
+
43
+ // ============================================
44
+ // Text Decoration Utilities
45
+ // ============================================
46
+ .text-decoration-dotted {
47
+ text-decoration-style: dotted !important;
48
+ }
49
+
50
+ .text-decoration-dashed {
51
+ text-decoration-style: dashed !important;
52
+ }
@@ -2,23 +2,22 @@
2
2
  import extension from './lib/extension.js';
3
3
  import LoggerLite from './lib/logger-lite.js';
4
4
 
5
+ // Firebase (static imports - dynamic import() doesn't work in service workers with webpack chunking)
6
+ import { initializeApp, getApp } from 'firebase/app';
7
+ import { getAuth, signInWithCustomToken, onAuthStateChanged } from 'firebase/auth';
8
+
5
9
  // Variables
6
10
  const serviceWorker = self;
7
11
 
8
12
  // Import build config at the top level (synchronous)
9
13
  importScripts('/build.js');
10
14
 
11
- // ⚠️⚠️⚠️ CRITICAL: Setup global listeners BEFORE importing Firebase ⚠️⚠️⚠️
15
+ // ⚠️⚠️⚠️ CRITICAL: Setup global listeners BEFORE any async operations ⚠️⚠️⚠️
12
16
  // https://stackoverflow.com/questions/78270541/cant-catch-fcm-notificationclick-event-in-service-worker-using-firebase-messa
17
+ // Note: ES6 static imports above are fine - they're hoisted and bundled by webpack.
18
+ // The issue is with importScripts() calls which must be synchronous at top-level.
13
19
  setupGlobalHandlers();
14
20
 
15
- // Import Firebase libraries at the top level (before any async operations)
16
- // ⚠️ importScripts MUST be called at top-level (synchronously) - it cannot be called inside functions or after async operations
17
- // importScripts(
18
- // 'https://www.gstatic.com/firebasejs/%%% firebaseVersion %%%/firebase-app-compat.js',
19
- // 'https://www.gstatic.com/firebasejs/%%% firebaseVersion %%%/firebase-messaging-compat.js',
20
- // );
21
-
22
21
  // Class
23
22
  class Manager {
24
23
  constructor() {
@@ -36,7 +35,8 @@ class Manager {
36
35
  this.app = this.config?.app?.id || 'extension';
37
36
  this.environment = this.config?.bem?.environment || 'production';
38
37
  this.libraries = {
39
- firebase: false,
38
+ firebase: null,
39
+ firebaseAuth: null,
40
40
  messaging: false,
41
41
  promoServer: false,
42
42
  };
@@ -62,6 +62,12 @@ class Manager {
62
62
  // Initialize Firebase
63
63
  this.initializeFirebase();
64
64
 
65
+ // Setup auth token listener (for cross-runtime auth)
66
+ this.setupAuthTokenListener();
67
+
68
+ // Setup auth storage listener (detect sign-out from pages)
69
+ this.setupAuthStorageListener();
70
+
65
71
  // Setup livereload
66
72
  this.setupLiveReload();
67
73
 
@@ -170,6 +176,211 @@ class Manager {
170
176
  .catch(error => this.logger.error('Failed to cache resources:', error));
171
177
  }
172
178
 
179
+ // Setup auth token listener (monitors tabs for auth tokens from website)
180
+ setupAuthTokenListener() {
181
+ // DEBUG: Log the full config to see what we have
182
+ console.log('[AUTH] setupAuthTokenListener called');
183
+ console.log('[AUTH] this.config:', this.config);
184
+ console.log('[AUTH] BEM_BUILD_JSON:', serviceWorker.BEM_BUILD_JSON);
185
+
186
+ // Get auth domain from config
187
+ // Structure is: this.config.firebase.app.config.authDomain
188
+ const authDomain = this.config?.firebase?.app?.config?.authDomain;
189
+
190
+ // Log config for debugging
191
+ this.logger.log('[AUTH] Config paths:', {
192
+ firebase_path: this.config?.firebase?.app?.config?.authDomain,
193
+ resolved: authDomain,
194
+ });
195
+
196
+ // Skip if no auth domain configured
197
+ if (!authDomain) {
198
+ this.logger.log('[AUTH] No authDomain configured, skipping auth token listener');
199
+ return;
200
+ }
201
+
202
+ // Log
203
+ this.logger.log(`[AUTH] Setting up auth token listener for domain: ${authDomain}`);
204
+
205
+ // Listen for tab URL changes
206
+ this.extension.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
207
+ // Only process when URL changes and is complete
208
+ if (changeInfo.status !== 'complete' || !tab.url) {
209
+ return;
210
+ }
211
+
212
+ // Parse the URL
213
+ let tabUrl;
214
+ try {
215
+ tabUrl = new URL(tab.url);
216
+ } catch (e) {
217
+ return;
218
+ }
219
+
220
+ // Log every tab update for auth domain matching
221
+ this.logger.log(`[AUTH] Tab updated: ${tabUrl.hostname} (looking for: ${authDomain})`);
222
+
223
+ // Check if this is our auth domain
224
+ if (tabUrl.hostname !== authDomain) {
225
+ return;
226
+ }
227
+
228
+ // Log - we found our domain
229
+ this.logger.log(`[AUTH] Auth domain matched! Checking for authToken param...`);
230
+
231
+ // Check for authToken param
232
+ const authToken = tabUrl.searchParams.get('authToken');
233
+ if (!authToken) {
234
+ this.logger.log(`[AUTH] No authToken param found in URL: ${tabUrl.href}`);
235
+ return;
236
+ }
237
+
238
+ // Get source tab ID to restore after auth
239
+ const authSourceTabId = tabUrl.searchParams.get('authSourceTabId');
240
+
241
+ // Log
242
+ this.logger.log('[AUTH] Auth token detected in tab:', tabId);
243
+
244
+ // Handle the auth token
245
+ this.handleAuthToken(authToken, tabId, authSourceTabId ? parseInt(authSourceTabId, 10) : null);
246
+ });
247
+ }
248
+
249
+ // Setup auth storage listener (detect sign-out from pages)
250
+ setupAuthStorageListener() {
251
+ this.extension.storage.onChanged.addListener((changes) => {
252
+ const authChange = changes['bxm:authState'];
253
+ if (!authChange) {
254
+ return;
255
+ }
256
+
257
+ this.logger.log('[AUTH] Storage auth state changed:', authChange.newValue ? 'signed in' : 'signed out');
258
+
259
+ // If storage was cleared (sign-out from a page) and we have Firebase initialized, sign out
260
+ if (!authChange.newValue && this.libraries.firebaseAuth) {
261
+ this.logger.log('[AUTH] Signing out background Firebase...');
262
+ this.libraries.firebaseAuth.signOut();
263
+ }
264
+ });
265
+
266
+ this.logger.log('[AUTH] Auth storage listener set up');
267
+ }
268
+
269
+ // Get or initialize Firebase auth (reuse existing instance)
270
+ getFirebaseAuth() {
271
+ // Return existing instance if available
272
+ if (this.libraries.firebaseAuth) {
273
+ return this.libraries.firebaseAuth;
274
+ }
275
+
276
+ // Get Firebase config
277
+ const firebaseConfig = this.config?.firebase?.app?.config;
278
+ if (!firebaseConfig) {
279
+ throw new Error('Firebase config not available');
280
+ }
281
+
282
+ // Try to get existing app or create new one
283
+ try {
284
+ this.libraries.firebase = getApp('bxm-auth');
285
+ } catch (e) {
286
+ this.libraries.firebase = initializeApp(firebaseConfig, 'bxm-auth');
287
+ }
288
+
289
+ // Get auth and set up state listener (only once)
290
+ this.libraries.firebaseAuth = getAuth(this.libraries.firebase);
291
+
292
+ // Set up auth state change listener (background is source of truth)
293
+ onAuthStateChanged(this.libraries.firebaseAuth, (user) => {
294
+ this.handleAuthStateChange(user);
295
+ });
296
+
297
+ return this.libraries.firebaseAuth;
298
+ }
299
+
300
+ // Handle Firebase auth state changes (source of truth for all contexts)
301
+ async handleAuthStateChange(user) {
302
+ this.logger.log('[AUTH] Auth state changed:', user?.email || 'signed out');
303
+
304
+ if (user) {
305
+ // User is signed in - get current stored state to preserve token
306
+ const result = await new Promise(resolve =>
307
+ this.extension.storage.get('bxm:authState', resolve)
308
+ );
309
+ const currentState = result['bxm:authState'] || {};
310
+
311
+ // Update auth state with current user info
312
+ const authState = {
313
+ token: currentState.token, // Preserve existing token
314
+ user: {
315
+ uid: user.uid,
316
+ email: user.email,
317
+ displayName: user.displayName,
318
+ photoURL: user.photoURL,
319
+ emailVerified: user.emailVerified,
320
+ },
321
+ timestamp: Date.now(),
322
+ };
323
+
324
+ await this.extension.storage.set({ 'bxm:authState': authState });
325
+ this.logger.log('[AUTH] Auth state synced to storage');
326
+ } else {
327
+ // User is signed out - clear storage
328
+ await this.extension.storage.remove('bxm:authState');
329
+ this.logger.log('[AUTH] Auth state cleared from storage');
330
+ }
331
+ }
332
+
333
+ // Handle auth token from website
334
+ async handleAuthToken(token, tabId, authSourceTabId = null) {
335
+ try {
336
+ // Log
337
+ this.logger.log('[AUTH] Processing auth token...');
338
+
339
+ // Get or initialize Firebase auth
340
+ const auth = this.getFirebaseAuth();
341
+
342
+ // Sign in with custom token
343
+ this.logger.log('[AUTH] Calling signInWithCustomToken...');
344
+ const userCredential = await signInWithCustomToken(auth, token);
345
+ const user = userCredential.user;
346
+
347
+ // Log
348
+ this.logger.log('[AUTH] Signed in successfully:', user.email);
349
+
350
+ // Save token to storage (user state will be synced by onAuthStateChanged)
351
+ const result = await new Promise(resolve =>
352
+ this.extension.storage.get('bxm:authState', resolve)
353
+ );
354
+ const currentState = result['bxm:authState'] || {};
355
+
356
+ await this.extension.storage.set({
357
+ 'bxm:authState': {
358
+ ...currentState,
359
+ token: token,
360
+ timestamp: Date.now(),
361
+ }
362
+ });
363
+
364
+ // Close the auth tab
365
+ await this.extension.tabs.remove(tabId);
366
+ this.logger.log('[AUTH] Auth tab closed');
367
+
368
+ // Reactivate the source tab if provided
369
+ if (authSourceTabId) {
370
+ try {
371
+ await this.extension.tabs.update(authSourceTabId, { active: true });
372
+ this.logger.log('[AUTH] Restored source tab:', authSourceTabId);
373
+ } catch (e) {
374
+ // Tab may have been closed, ignore
375
+ this.logger.log('[AUTH] Could not restore source tab (may be closed):', authSourceTabId);
376
+ }
377
+ }
378
+
379
+ } catch (error) {
380
+ this.logger.error('[AUTH] Error handling auth token:', error);
381
+ }
382
+ }
383
+
173
384
  // Setup livereload
174
385
  setupLiveReload() {
175
386
  // Quit if not in dev mode
package/dist/content.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // Libraries
2
2
  import extension from './lib/extension.js';
3
3
  import LoggerLite from './lib/logger-lite.js';
4
+ import Affiliatizer from './lib/affiliatizer.js';
4
5
 
5
6
  // Class
6
7
  class Manager {
@@ -17,7 +18,7 @@ class Manager {
17
18
  this.extension = extension;
18
19
  this.messenger = null;
19
20
  this.logger = new LoggerLite('content');
20
- this.affiliatizer = (await import('./lib/affiliatizer.js')).default.initialize(this);
21
+ this.affiliatizer = Affiliatizer.initialize(this);
21
22
 
22
23
  // Log
23
24
  this.logger.log('Initialized!', this);
@@ -18,5 +18,9 @@ EDGE_PRODUCT_ID="your-product-id"
18
18
  EDGE_CLIENT_ID="your-client-id"
19
19
  EDGE_API_KEY="your-api-key"
20
20
 
21
+ # Opera Add-ons
22
+ # NOTE: Opera does not have a publishing API. Extensions must be submitted manually at:
23
+ # https://addons.opera.com/developer/
24
+
21
25
  # ========== Custom Values ==========
22
26
  # ...
@@ -1,11 +1,9 @@
1
1
  {
2
- app: {
3
- id: 'ultimate-jekyll',
4
- },
5
2
  theme: {
6
3
  id: 'classy',
7
4
  },
8
5
  brand: {
6
+ id: 'ultimate-jekyll',
9
7
  name: 'Ultimate Jekyll',
10
8
  url: 'https://ultimate-jekyll.itwcreativeworks.com',
11
9
  contact: {
@@ -25,13 +23,13 @@
25
23
  secret: '',
26
24
  },
27
25
  firebaseConfig: {
28
- apiKey: 'AIzaSyDzy5BG9-i9RmymyFx6mfwSgaRioyfa0Cc',
29
- authDomain: 'ultimate-jekyll.itwcreativeworks.com',
30
- databaseURL: 'https://ultimate-jekyll.firebaseio.com',
31
- projectId: 'ultimate-jekyll',
32
- storageBucket: 'ultimate-jekyll.appspot.com',
33
- messagingSenderId: '837186164890',
34
- appId: '1:837186164890:web:f1f59569d661078c',
35
- measurementId: 'G-FR3X7X4KYN',
26
+ apiKey: 'AIzaSyD1MvM-EQC_eVWqaTe5ImWALwhBr7iXgh8',
27
+ authDomain: 'universal-auth.itwcreativeworks.com',
28
+ databaseURL: 'https://itwcw-universal-auth.firebaseio.com',
29
+ projectId: 'itwcw-universal-auth',
30
+ storageBucket: 'itwcw-universal-auth.appspot.com',
31
+ messagingSenderId: '195162582358',
32
+ appId: '1:195162582358:web:6f9320c516d9ab4838baf7',
33
+ measurementId: 'G-W08HGRW4S',
36
34
  },
37
35
  }
package/dist/gulp/main.js CHANGED
@@ -9,6 +9,10 @@ const glob = require('glob').globSync;
9
9
  // Load package
10
10
  const package = Manager.getPackage('main');
11
11
  const project = Manager.getPackage('project');
12
+ const projectRoot = Manager.getRootPath('project');
13
+
14
+ // Load .env file from project root
15
+ require('dotenv').config({ path: path.join(projectRoot, '.env') });
12
16
 
13
17
  // Log
14
18
  logger.log('Starting...', argv);
@@ -4,7 +4,7 @@ const logger = Manager.logger('audit');
4
4
  const path = require('path');
5
5
  const jetpack = require('fs-jetpack');
6
6
  const { series } = require('gulp');
7
- const chalk = require('chalk');
7
+ const chalk = require('chalk').default;
8
8
 
9
9
  // Load package
10
10
  const package = Manager.getPackage('main');
@@ -18,6 +18,9 @@ const config = Manager.getConfig('project');
18
18
  const rootPathPackage = Manager.getRootPath('main');
19
19
  const rootPathProject = Manager.getRootPath('project');
20
20
 
21
+ // Constants
22
+ const LOUD = process.env.BXM_LOUD_LOGS === 'true';
23
+
21
24
  // Get clean versions
22
25
  // const cleanVersions = { versions: Manager.getCleanVersions()};
23
26
  const cleanVersions = { versions: package.engines };
@@ -434,7 +437,11 @@ function customTransform() {
434
437
 
435
438
  // Skip if instructed
436
439
  if (options.skip || (!options.overwrite && exists && !options.merge && !options.mergeLines)) {
437
- logger.log(`Skipping file: ${relativePath}`);
440
+ // Log if loud is enabled
441
+ if (LOUD) {
442
+ logger.log(`Skipping file: ${relativePath}`);
443
+ }
444
+
438
445
  return callback();
439
446
  }
440
447